Scala performance FAQ: Is there a reliable way to determine the number of processors (CPUs) or cores when writing Scala code?
Solution
As I was reading this article on ZIO performance tuning, I decided to look into whether there is a reliable way to determine the number of CPUs or cores on a computer using Scala (and therefore Java, Kotlin, and other JVM languages).
The solution is that there seems to be a reliable way to determine the number of CPUs or cores in Scala. In short, use the Java Runtime
class to get the information. This Scala example shows how to do it:
// get the runtime instance
val runtime = Runtime.getRuntime()
// get the number of available processors (cores)
val numProcessorsOrCores = runtime.availableProcessors()
// print the number of available processors (cores)
println(s"# of available processors/cores: $numProcessorsOrCores")
The information retrieved using Runtime.getRuntime().availableProcessors()
is considered to be accurate across different platforms. It provides the number of processors available to the Java Virtual Machine (JVM), which reflects the actual number of physical or logical cores available on the machine where the JVM is running.
Verification
To verify this, I tested the solution on my own MacBook, and saw these results in the Scala REPL:
scala> val runtime = Runtime.getRuntime()
val runtime: Runtime = java.lang.Runtime@53e28097
scala> val numProcessorsOrCores = runtime.availableProcessors()
val numProcessorsOrCores: Int = 12
scala> println(s"# of available processors/cores: $numProcessorsOrCores")
# of available processors/cores: 12
My macOS System Report shows the same information:
Total Number of Cores: 12 (6 performance and 6 efficiency)
Therefore, this solution appears to be correct, at least on this computer. (I don’t have any other computers I can test on at the moment.)
Notes
The ZIO code that prompted me to look into this is:
ZIO
.foreachPar(1 to 1000)(_ => doSomething)
.withParallelism(16)
The withParallelism
method lets you set the number of ZIO fibers that foreachPar
will use in that expression.
The issue with this code is that if you have 12 cores and are creating 16 fibers, your code may be inefficient because you’re attempting to create more fibers than CPUs/cores they can run on.
Instead of hard-coding 16 into your code, you’ll probably be better off determining the numbers of cores available when your application starts — or when you’re about to run this code, if the number of cores can vary over time — and then use a number equal to or less than that when calling withParallelism
.
Beyond that, these notes may also be important:
- The number of processors/cores may vary depending on the operating system’s configuration and resource allocation.
- On systems with hyperthreading, it typically returns the number of logical cores, which may be twice the number of physical cores. This may be higher than the number of physical cores.
- The method returns the number of processors available to the JVM at the time of the query. This number can change over time in virtualized environments.
There are also these specific notes about Containerization, CPU Affinity, and Consistency Across Platforms:
- Containerization: In containerized environments like Docker, it usually returns the number of CPUs allocated to the container, not the host system.
- CPU Affinity and Resource Management: In environments like virtual machines or containers, the number of available processors might be limited by the host system’s configuration or resource management policies. The JVM will only be aware of the processors it has been allocated — not the total number of physical cores on the host.
- Consistency Across Platforms: The
availableProcessors()
method is part of the Java standard library and should work consistently across different operating systems (Linux, macOS, Windows, etc.). In theory, it abstracts the underlying platform-specific details to provide a unified way of obtaining this information, but I have seen warnings about issues with different operating systems, OS versions, JVM versions, and different JVM implementations.