In modernen Computersystemen kann es mehrere CPUs geben und jede CPU kann mehrere Kerne haben. Um die Funktionen moderner CPUs voll auszunutzen, wurde in JAVA Multithreading eingeführt. Verschiedene Threads können gleichzeitig in verschiedenen CPUs oder verschiedenen CPU-Kernen laufen. Aber für JAVA-Programmierer können sie steuern, wie viele Threads sie erstellen, aber auf welcher CPU die Threads ausgeführt werden, ist eine Blackbox und im Allgemeinen schwer zu wissen.
Aber wenn verschiedene CPU-Kerne denselben Thread planen, kann es zu Leistungseinbußen durch CPU-Umschaltung kommen. Unter normalen Umständen ist dieser Verlust relativ gering, aber wenn Ihr Programm besonders besorgt über den Verlust ist, der durch diese Art von CPU-Umschaltung verursacht wird, können Sie die Java Thread Affinity ausprobieren, über die ich heute sprechen werde.
Java-Thread-Affinität Es wird verwendet, um Threads im JAVA-Code an bestimmte Kerne der CPU zu binden, um die Programmausführungsleistung zu verbessern.
Um mit der zugrunde liegenden CPU zu interagieren, verwendet Java Thread Affinity natürlich definitiv die Methode zur Interaktion mit JAVA und nativen Methoden. Obwohl JNI die offizielle Methode zur Interaktion mit JAVA und nativen Methoden ist, ist JNI nicht einfach zu verwenden. Eher umständlich. Daher verwendet Java Thread Affinity tatsächlich JNA. JNA ist eine auf JNI basierende Bibliothek, die mit nativen Methoden interagiert.
Lassen Sie uns zunächst einige Konzepte in der CPU vorstellen, nämlich CPU, CPU-Sockel und CPU-Kern.
Das erste ist die CPU. Der vollständige Name der CPU ist Zentraleinheit, auch Zentraleinheit genannt, die der Schlüsselkern ist, der für die Aufgabenverarbeitung verwendet wird.
Was ist also ein CPU-Sockel? Der sogenannte Sockel ist der Steckplatz, in den die CPU gesteckt wird. Wenn Sie einen Desktop-Computer zusammengebaut haben, sollten Sie wissen, dass die CPU auf dem Sockel installiert ist.
CPU-Kern bezieht sich auf die Anzahl der Kerne in der CPU. Vor langer Zeit waren alle CPUs Single-Core-Prozessoren. Mit der Entwicklung der Multi-Core-Technologie kann eine CPU jedoch mehrere Kerne enthalten sind die eigentliche Geschäftsabwicklungseinheit.
Wenn Sie sich auf einem Linux-Computer befinden, können Sie den CPU-Status des Systems mit dem Befehl lscpu überprüfen, wie unten gezeigt:
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
Aus der obigen Ausgabe können wir erkennen, dass dieser Server einen Socket hat, und zwar jeden Socket hat einen Kern, jeder Kern kann 1 Thread gleichzeitig verarbeiten.
Diese CPU-Informationen können als CPU-Layout bezeichnet werden. Unter Linux werden die CPU-Layoutinformationen in /proc/cpuinfo gespeichert.
In Java Thread Affinity gibt es eine CpuLayout-Schnittstelle, die diesen Informationen entspricht:
public interface CpuLayout { int cpus(); int sockets(); int coresPerSocket(); int threadsPerCore(); int socketId(int cpuId); int coreId(int cpuId); int threadId(int cpuId); }
Gemäß den CPU-Layoutinformationen bietet AffinityStrategies hauptsächlich einige grundlegende Affinitätsstrategien zum Anordnen der Verteilungsbeziehung zwischen verschiedenen Threads Es gibt die folgenden Typen:
SAME_CORE - 运行在同一个core中。 SAME_SOCKET - 运行在同一个socket中,但是不在同一个core上。 DIFFERENT_SOCKET - 运行在不同的socket中 DIFFERENT_CORE - 运行在不同的core上 ANY - 任何情况都可以
Diese Strategien werden auch anhand der SocketId und coreId von CpuLayout unterschieden. Nehmen wir SAME_CORE als Beispiel und drücken Sie auf die spezifische Implementierung:
SAME_CORE { @Override public boolean matches(int cpuId, int cpuId2) { CpuLayout cpuLayout = AffinityLock.cpuLayout(); return cpuLayout.socketId(cpuId) == cpuLayout.socketId(cpuId2) && cpuLayout.coreId(cpuId) == cpuLayout.coreId(cpuId2); } }
Affinitätsstrategien können in der Reihenfolge sein, in der vorherigen Strategie. Wird zuerst übereinstimmen, wenn nicht Die zweite Strategie wird ausgewählt und so weiter.
Als nächstes schauen wir uns die spezifische Verwendung von Affinity an. Die erste besteht darin, eine CPU-Sperre zu erhalten. Vor JAVA7 können wir so schreiben:
AffinityLock al = AffinityLock.acquireLock(); try { // do some work locked to a CPU. } finally { al.release(); }
Nach JAVA7 können wir so schreiben:
try (AffinityLock al = AffinityLock.acquireLock()) { // do some work while locked to a CPU. }
try (AffinityLock al = AffinityLock.acquireCore()) { // do some work while locked to a CPU. }
public AffinityLock acquireLock(AffinityStrategy... strategies) { return acquireLock(false, cpuId, strategies); }
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>() { @Override public Void call() throws InterruptedException { Thread.sleep(100); return null; } }); Thread.sleep(200); System.out.println("\nThe assignment of CPUs is\n" + AffinityLock.dumpLocks()); ES.shutdown(); ES.awaitTermination(1, TimeUnit.SECONDS);
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
new AffinityThreadFactory("bg", SAME_CORE)
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
可以看到只有第一个线程绑定了CPU,和之前的分析相匹配。
上面我们提到的AffinityLock的acquireLock方法其实还可以接受一个CPU id参数,直接用来获得传入CPU id的lock。这样后续线程就可以在指定的CPU上运行。
public static AffinityLock acquireLock(int cpuId) { return acquireLock(true, cpuId, AffinityStrategies.ANY); }
实时上这种Affinity是存放在BitSet中的,BitSet的index就是cpu的id,对应的value就是是否获得锁。
先看下setAffinity方法的定义:
public static void setAffinity(int cpu) { BitSet affinity = new BitSet(Runtime.getRuntime().availableProcessors()); affinity.set(cpu); setAffinity(affinity); }
再看下setAffinity的使用:
long currentAffinity = AffinitySupport.getAffinity(); Affinity.setAffinity(1L << 5); // lock to CPU 5.
注意,因为BitSet底层是用Long来进行数据存储的,所以这里的index是bit index,所以我们需要对十进制的CPU index进行转换。
Das obige ist der detaillierte Inhalt vonWie binde ich einen Thread in Java an eine bestimmte CPU?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!