Comprehensive and straightforward Linux thread affinity in Java so that you can pin your critical threads to isolated CPU cores for maximum performance. To pin a thread to a CPU logical processor id (or CPU core id) all you have to do is:
int procId = 1;
Affinity.set(procId);
You can also set the affinity to more than one processor id and the kernel will decide where to pin the thread:
int[] procIds = { 1, 2, 3 };
Affinity.set(procIds);
If you run the included script ./bin/cpuInfo.sh
you get:
$ ./bin/cpuInfo.sh java -cp target/coralaffinity-all.jar com.coralblocks.coralaffinity.CpuInfo isEnabled: true isAvailable: true numberOfProcessors: 16 => procIds=0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 physicalChips: Chip0, Chip1 processorsPerChip: Chip0=[0,1,2,3,4,5,6,7] Chip1=[8,9,10,11,12,13,14,15] isHyperthreadingOn: true hyperthreadedPairs: [0, 1], [2, 3], [4, 5], [6, 7], [8, 9], [10, 11], [12, 13], [14, 15] cpuBitmasksFound: 9 => 64, 128, 192, 256, 320, 384, 448, 512, 1024 (in bits) chosenCpuBitmaskSize: 64 bits nonIsolatedCpuBitmask: 64515 (1111110000000011) => procIds=0,1,10,11,12,13,14,15 isolatedCpuBitmask: 1020 (1111111100) => procIds=2,3,4,5,6,7,8,9
-
isEnabled: You can easily disable CoralAffinity during testing and development through
-DcoralAffinityEnabled=false
orexport coralAffinityEnabled=false
. -
isAvailable: CoralAffinity automatically checks if it is available and can run in the machine it is being used. If it is not available your code will still run without any problems, like if CoralAffinity was disabled.
-
numberOfProcessors: The number of logical processors in the machine. If the machine is using hyperthreading, the number of logical processors will be twice the number of physical CPU cores.
-
physicalChips: The physical chips (microprocessors) present in the machine.
-
processorsPerChip: The logical processors (processor ids) present in each of the physical chips.
-
isHyperthreadingOn: If the machine is using hyperthreading, each physical CPU core will be able to run two threads at the same time.
-
hyperthreadedPairs: With hyperthreading, each physical CPU core will have two logical processors. These are the pairs of logical processors per CPU core.
-
cpuBitmasksFound: The different sizes (in bits) that can be used for the CPU affinity mask.
CoralAffinity supports up to 1024 logical processors and can be easily extended to support more.
-
chosenCpuBitmaskSize: The chosen CPU affinity mask size (in bits) to be used by CoralAffinity. This will always be greather or equal the number of logical processors, as each processor is represented by one bit.
-
nonIsolatedCpuBitmask: The CPU mask and the processor ids that are not isolated from the kernel scheduler. If no thread affinity is set, the kernel will execute the thread on one of these logical processors.
-
isolatedCpuBitmask: The CPU mask and the processor ids that are isolated from the kernel scheduler. The kernel scheduler will not touch these processors unless affinity is set.
When calling the Affinity.set(int ... procIds)
method, you can check its return value, which is the Affinity.SchedResult object. See the example below:
int procId = 1;
SchedResult schedResult = Affinity.set(procId);
if (schedResult.isOk()) {
System.out.println("Thread pinned!"
+ " threadName="+ Thread.currentThread().getName()
+ " procId=" + procId);
} else {
System.out.println("Could not pin thread!"
+ " threadName="+ Thread.currentThread().getName()
+ " procId=" + procId
+ " schedResult=" + schedResult);
}
You can use the method setSchedulableCpus()
from Affinity to set the affinity mask to be any non-isolated cpu core, in other words, cpu cores that are schedulable by the kernel. Below we list this method documentation for reference:
/**
* Sets the thread affinity of the calling thread to be any schedulable CPU logical
* processor which is not isolated from the kernel scheduler.
* This method is useful when a thread which is already pinned to an isolated CPU
* logical processor wants to spawn a new thread but does not want this new thread
* to be pinned to the same isolated CPU logical processor, which is what happens
* by default.
*
* @return a {@see SchedResult} object with the result of the call
*/
public synchronized static final SchedResult setSchedulableCpus();
You can use the included script ./bin/pinThread.sh
to execute the sample PinThread.java to test if CoralAffinity is working on your Linux machine.
NOTE: After running the top
command on Linux, press 1
to see the list of logical processors.
This is done through the isolcpus
kernel parameter. Below the basics:
First, determine the CPU cores available on your system:
cat /proc/cpuinfo | grep processor
To isolate specific CPU cores, you need to add the isolcpus
parameter to your kernel command line.
a) Open the GRUB configuration file:
sudo vi /etc/default/grub
b) Find the line starting with GRUB_CMDLINE_LINUX_DEFAULT
or GRUB_CMDLINE_LINUX
.
c) Append the isolcpus
parameter, specifying the core numbers you want to isolate. For example, to isolate cores 2 and 3:
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash isolcpus=2,3"
d) Save the file and update GRUB:
sudo update-grub
or, for some distributions:
sudo grub-mkconfig -o /boot/grub/grub.cfg
Reboot for changes to take effect:
sudo reboot
After rebooting, check the isolated CPUs using:
cat /sys/devices/system/cpu/isolated
Alternatively, you can verify with:
cat /proc/cmdline