CPU bound thread java thread affinity

Help me to the Internet cafe 2022-05-14 14:39:44 阅读数:156


brief introduction

In modern computer systems , There can be multiple CPU, Every CPU There can be multiple cores . In order to make the most of modern CPU The function of ,JAVA Multithreading is introduced in , Different threads can work in different at the same time CPU Or different CPU Run in the nuclear . But for JAVA For program apes, how many threads can be created can be controlled by themselves , But where does the thread run CPU On , It's a black box , Generally speaking, it's hard to know .

But if it's different CPU Check the same thread for scheduling , There may be CPU Performance loss caused by switching . In general, this loss is relatively small , But if your program is particularly concerned about this CPU Loss caused by switching , Then try today's talk Java Thread Affinity.

Java Thread Affinity brief introduction

java thread Affinity It's used to put JAVA The thread in the code is bound to CPU On specific nuclei , Used to improve the performance of the program .

Obviously , If you want to be with the bottom CPU Interact ,java thread Affinity It must be used JAVA and native Method to interact with ,JNI Although it is JAVA Official JAVA and native Method to interact with , however JNI It is cumbersome to use . therefore java thread Affinity The actual use is JNA,JNA Is in JNI A kind of and improved on the basis of native Method to interact with .

Let's first introduce CPU There are several concepts in this paper , Namely CPU,CPU socket and CPU core.

First of all CPU,CPU The full name of is central processing unit, Also called central processing unit , Is the key core for task processing .

So what is CPU socket Well ? So-called socket Just plug in CPU Slot for , Students who have assembled desktop computers should know ,CPU It's installed in Socket Upper .

CPU Core refer to CPU The number of cores in , A long time ago CPU It's all single core , But with the development of multi-core technology , One CPU Can contain multiple cores , and CPU The core of is the real business processing unit .

If you are linux On the machine , Then you can use lscpu Command to view the system CPU situation , As shown below :

Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 1
On-line CPU(s) list: 0
Thread(s) per core: 1
Core(s) per socket: 1
Socket(s): 1
NUMA node(s): 1
Vendor ID: GenuineIntel
CPU family: 6
Model: 94
Model name: Intel(R) Xeon(R) Gold 6148 CPU @ 2.40GHz
Stepping: 3
CPU MHz: 2400.000
BogoMIPS: 4800.00
Hypervisor vendor: KVM
Virtualization type: full
L1d cache: 32K
L1i cache: 32K
L2 cache: 4096K
L3 cache: 28160K
NUMA node0 CPU(s): 0
Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl eagerfpu pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single fsgsbase bmi1 hle avx2 smep bmi2 erms invpcid rtm mpx avx512f avx512dq rdseed adx smap avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 arat

From the output above we can see , This server has a socket, Every socket There is one core, Every core Can handle at the same time 1 Threads .

these CPU Information can be called CPU layout. stay linux in CPU Of layout Information is stored in /proc/cpuinfo Medium .

stay Java Thread Affinity There is one of them. CpuLayout The interface is used to correspond to this information :

public interface CpuLayout {
int cpus();
int sockets();
int coresPerSocket();
int threadsPerCore();
int socketId(int cpuId);
int coreId(int cpuId);
int threadId(int cpuId);

according to CPU layout Information about , AffinityStrategies Provides some basic Affinity Strategy , Used to arrange different thread Distribution relationship between , There are mainly the following :

 SAME_CORE - Run in the same core in .
SAME_SOCKET - Run in the same socket in , But not in the same core On .
DIFFERENT_SOCKET - Run in different socket in
DIFFERENT_CORE - Run in different core On
ANY - In any case

These strategies are also based on CpuLayout Of socketId and coreId To make a distinction , We use SAME_CORE For example , Press its specific implementation :

public boolean matches(int cpuId, int cpuId2) {
CpuLayout cpuLayout = AffinityLock.cpuLayout();
return cpuLayout.socketId(cpuId) == cpuLayout.socketId(cpuId2) &&
cpuLayout.coreId(cpuId) == cpuLayout.coreId(cpuId2);

Affinity Strategies can be sequential , The previous strategy will first match , If it doesn't match, the second strategy will be selected , And so on .

AffinityLock Use

Let's take a look Affinity Specific use of , The first is to get a CPU Of lock, stay JAVA7 Before , We can write this way :

AffinityLock al = AffinityLock.acquireLock();
try {
// do some work locked to a CPU.
} finally {

stay JAVA7 after , It can be written like this :

try (AffinityLock al = AffinityLock.acquireLock()) {
// do some work while locked to a CPU.

acquireLock Method can get any available... For the thread cpu. This is a coarse-grained lock. If you want to get fine-grained core, It can be used acquireCore:

try (AffinityLock al = AffinityLock.acquireCore()) {
// do some work while locked to a CPU.

acquireLock One more bind Parameters , Indicates whether to bind the current thread to the obtained cpu lock On , If bind Parameters =true, So the current thread Will be in acquireLock From CPU Up operation . If bind Parameters =false, Express acquireLock At some point in the future bind.

We mentioned above AffinityStrategy, This AffinityStrategy It can be used as acquireLock Parameter usage of :

 public AffinityLock acquireLock(AffinityStrategy... strategies) {
return acquireLock(false, cpuId, strategies);

By calling the current AffinityLock Of acquireLock Method , The current thread can be allocated with the previous thread lock Strategy related AffinityLock.

AffinityLock One is also provided dumpLocks Method , Used to view the current CPU and thread The binding state of . Let's take an example :

private static final ExecutorService ES = Executors.newFixedThreadPool(4,
new AffinityThreadFactory("bg", SAME_CORE, DIFFERENT_SOCKET, ANY));
for (int i = 0; i < 12; i++)
ES.submit(new Callable<Void>() {
public Void call() throws InterruptedException {
return null;
System.out.println("\nThe assignment of CPUs is\n" + AffinityLock.dumpLocks());
ES.awaitTermination(1, TimeUnit.SECONDS);

In the above code , We created one 4 A pool of threads , Corresponding ThreadFactory yes AffinityThreadFactory, Name the thread pool bg, And distributed 3 individual AffinityStrategy. It means to assign to the same... First core On , Then go to different socket On , Finally, any available CPU.

Then the specific implementation process , We submitted 12 Threads , But our Thread pool Only up to 4 Threads , Predictably, , AffinityLock.dumpLocks Method returns only 4 A thread will bind CPU, Let's see :

The assignment of CPUs is
0: CPU not available
1: Reserved for this application
2: Reserved for this application
3: Reserved for this application
4: Thread[bg-4,5,main] alive=true
5: Thread[bg-3,5,main] alive=true
6: Thread[bg-2,5,main] alive=true
7: Thread[bg,5,main] alive=true

As you can see from the output ,CPU0 Is not available . other 7 individual CPU Is available , But only bound 4 Threads , This matches our previous analysis .

Next , We put AffinityThreadFactory Of AffinityStrategy Revise it , As shown below :

new AffinityThreadFactory("bg", SAME_CORE)

Indicates that the thread will only be bound to the same core in , Because in the current hardware , One core At the same time, it can only support the binding of one thread , Therefore, it can be predicted that the final result will only bind to one thread , The operation results are as follows :

The assignment of CPUs is
0: CPU not available
1: Reserved for this application
2: Reserved for this application
3: Reserved for this application
4: Reserved for this application
5: Reserved for this application
6: Reserved for this application
7: Thread[bg,5,main] alive=true

You can see that only the first thread is bound CPU, Match the previous analysis .

Use API Direct distribution CPU

As we mentioned above AffinityLock Of acquireLock In fact, the method can also accept a CPU id Parameters , Directly used to get incoming CPU id Of lock. In this way, subsequent threads can be in the specified CPU Up operation .

 public static AffinityLock acquireLock(int cpuId) {
return acquireLock(true, cpuId, AffinityStrategies.ANY);

In real time Affinity Is stored in BitSet Medium ,BitSet Of index Namely cpu Of id, Corresponding value Is whether to get the lock .

Let's take a look at setAffinity Method definition :

 public static void setAffinity(int cpu) {
BitSet affinity = new BitSet(Runtime.getRuntime().availableProcessors());

Let's take a look at setAffinity Use :

long currentAffinity = AffinitySupport.getAffinity();
Affinity.setAffinity(1L << 5); // lock to CPU 5.

Be careful , because BitSet The bottom layer is made of Long To store data , So here index yes bit index, So we need to change the decimal CPU index convert .

版权声明:本文为[Help me to the Internet cafe]所创,转载请带上原文链接,感谢。 https://javamana.com/2022/134/202205141436333808.html