Heim > Java > javaLernprogramm > Hauptteil

Wie binde ich einen Thread in Java an eine bestimmte CPU?

WBOY
Freigeben: 2023-05-09 23:58:17
nach vorne
2070 Leute haben es durchsucht

Einführung

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.

Einführung in Java Thread Affinity

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
Nach dem Login kopieren

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);
}
Nach dem Login kopieren

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 - 任何情况都可以
Nach dem Login kopieren

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);
        }
    }
Nach dem Login kopieren

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.

Verwendung von AffinityLock

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 dem Login kopieren

Nach JAVA7 können wir so schreiben:

try (AffinityLock al = AffinityLock.acquireLock()) {
    // do some work while locked to a CPU.
}
Nach dem Login kopieren
acquireLock-Methode Jede verfügbare CPU kann für den Thread abgerufen werden. Dies ist eine grobkörnige Sperre. Wenn Sie einen feinkörnigen Kern erhalten möchten, können Sie acquireCore:

try (AffinityLock al = AffinityLock.acquireCore()) {
    // do some work while locked to a CPU.
}
Nach dem Login kopieren

acquireLock verwenden. Es verfügt auch über einen Bind-Parameter, der angibt, ob der aktuelle Thread an die erworbene CPU-Sperre gebunden werden soll. Wenn der Bind-Parameter = true ist, wird der aktuelle Thread dies tun Seien Sie in Run auf der CPU, die Sie in acquireLock erhalten haben. Wenn der Bindungsparameter=false ist, bedeutet dies, dass acquireLock irgendwann in der Zukunft gebunden wird.

Wir haben oben AffinityStrategy erwähnt. Diese AffinityStrategy kann als Parameter von acquireLock verwendet werden:

public AffinityLock acquireLock(AffinityStrategy... strategies) {
 return acquireLock(false, cpuId, strategies);
    }
Nach dem Login kopieren

Durch Aufrufen der acquireLock-Methode des aktuellen AffinityLock kann dem aktuellen Thread ein AffinityLock zugewiesen werden, das sich auf die vorherige Sperrstrategie bezieht.

AffinityLock bietet außerdem eine dumpLocks-Methode zum Anzeigen des aktuellen Bindungsstatus der CPU und des Threads. Nehmen wir ein Beispiel:

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);
Nach dem Login kopieren

Im obigen Code haben wir einen Thread-Pool mit 4 Threads erstellt, die entsprechende ThreadFactory ist AffinityThreadFactory, den Thread-Pool bg genannt und 3 AffinityStrategy zugewiesen. Dies bedeutet, dass zuerst derselbe Kern, dann ein anderer Sockel und schließlich eine beliebige verfügbare CPU zugewiesen wird.

Dann haben wir während des spezifischen Ausführungsprozesses 12 Threads übermittelt, aber unser Thread-Pool hat nur maximal 4 Threads. Es ist vorhersehbar, dass nur 4 Threads in den von der AffinityLock.dumpLocks-Methode zurückgegebenen Ergebnissen an die CPU gebunden werden. Werfen wir einen Blick darauf:

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
Nach dem Login kopieren

Wie Sie der Ausgabe entnehmen können, ist CPU0 nicht verfügbar. Die anderen 7 CPUs sind verfügbar, es sind jedoch nur 4 Threads an sie gebunden, was unserer vorherigen Analyse entspricht.

Als nächstes ändern wir die AffinityStrategy von AffinityThreadFactory wie folgt:

new AffinityThreadFactory("bg", SAME_CORE)
Nach dem Login kopieren

bedeutet, dass Threads nur an denselben Kern gebunden werden, da in der aktuellen Hardware ein Kern nur die Bindung eines Threads gleichzeitig unterstützen kann. Daher kann vorhergesagt werden, dass das Endergebnis nur an einen Thread gebunden ist und die laufenden Ergebnisse wie folgt lauten:

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,和之前的分析相匹配。

使用API直接分配CPU

上面我们提到的AffinityLock的acquireLock方法其实还可以接受一个CPU id参数,直接用来获得传入CPU id的lock。这样后续线程就可以在指定的CPU上运行。

    public static AffinityLock acquireLock(int cpuId) {
        return acquireLock(true, cpuId, AffinityStrategies.ANY);
    }
Nach dem Login kopieren

实时上这种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);
    }
Nach dem Login kopieren

再看下setAffinity的使用:

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

注意,因为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!

Verwandte Etiketten:
Quelle:yisu.com
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage