Rumah  >  Artikel  >  Java  >  Bagaimana untuk mengikat benang ke CPU tertentu di Jawa?

Bagaimana untuk mengikat benang ke CPU tertentu di Jawa?

WBOY
WBOYke hadapan
2023-05-09 23:58:172068semak imbas

Pengenalan

Dalam sistem komputer moden, mungkin terdapat berbilang CPU dan setiap CPU boleh mempunyai berbilang teras. Untuk menggunakan sepenuhnya fungsi CPU moden, multi-threading telah diperkenalkan dalam JAVA Benang yang berbeza boleh dijalankan dalam CPU yang berbeza atau teras CPU yang berbeza pada masa yang sama. Tetapi untuk pengaturcara JAVA, mereka boleh mengawal bilangan utas yang mereka cipta, tetapi CPU mana benang dijalankan ialah kotak hitam, dan secara amnya sukar untuk diketahui.

Walau bagaimanapun, jika teras CPU yang berbeza menjadualkan urutan yang sama, mungkin terdapat kehilangan prestasi yang disebabkan oleh penukaran CPU. Dalam keadaan biasa, kerugian ini agak kecil, tetapi jika program anda amat mengambil berat tentang kehilangan yang disebabkan oleh pensuisan CPU jenis ini, maka anda boleh mencuba Java Thread Affinity yang akan saya bincangkan hari ini.

Pengenalan kepada Java Thread Affinity

java thread Affinity digunakan untuk mengikat thread dalam kod JAVA ke teras tertentu CPU untuk meningkatkan prestasi menjalankan program.

Jelas sekali, untuk berinteraksi dengan CPU asas, java thread Affinity pasti akan menggunakan kaedah JAVA dan native untuk berinteraksi Walaupun JNI ialah kaedah rasmi JAVA untuk berinteraksi dengan kaedah asli, JNI agak menyusahkan untuk digunakan. Oleh itu, java thread Affinity sebenarnya menggunakan JNA ialah perpustakaan yang dipertingkatkan berdasarkan JNI dan berinteraksi dengan kaedah asli.

Pertama, mari kita perkenalkan beberapa konsep dalam CPU, iaitu CPU, soket CPU dan teras CPU.

Pertama ialah CPU Nama penuh CPU ialah unit pemprosesan pusat, juga dipanggil unit pemprosesan pusat, iaitu teras utama yang digunakan untuk pemprosesan tugas.

Jadi apakah soket CPU? Soket yang dipanggil ialah slot di mana CPU dimasukkan Jika anda telah memasang komputer meja, anda harus tahu bahawa CPU dipasang pada soket.

Teras CPU merujuk kepada bilangan teras dalam CPU Pada masa dahulu, CPU adalah teras tunggal Namun, dengan pembangunan teknologi berbilang teras, CPU boleh mengandungi berbilang teras dan teras dalam CPU adalah Unit sebenar untuk pemprosesan perniagaan.

Jika anda menggunakan mesin Linux, anda boleh menggunakan perintah lscpu untuk menyemak status CPU sistem, seperti yang ditunjukkan di bawah:

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

Daripada output di atas kita dapat melihat bahawa pelayan ini mempunyai soket, setiap soket mempunyai teras, dan setiap teras boleh memproses 1 benang pada masa yang sama.

Maklumat CPU ini boleh dipanggil susun atur CPU. Di Linux, maklumat susun atur CPU disimpan dalam /proc/cpuinfo.

Terdapat antara muka CpuLayout dalam Java Thread Affinity untuk sepadan dengan maklumat ini:

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

Menurut maklumat susun atur CPU, AffinityStrategies menyediakan beberapa strategi Affinity asas untuk mengatur hubungan pengedaran yang berbeza antara thread terutamanya sertakan yang berikut:

    SAME_CORE - 运行在同一个core中。
    SAME_SOCKET - 运行在同一个socket中,但是不在同一个core上。
    DIFFERENT_SOCKET - 运行在不同的socket中
    DIFFERENT_CORE - 运行在不同的core上
    ANY - 任何情况都可以

Strategi ini juga dibezakan berdasarkan socketId dan coreId CpuLayout Kami mengambil SAME_CORE sebagai contoh dan menekan pelaksanaan khususnya:

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);
        }
    }

Strategi perkaitan boleh teratur. . Strategi sebelumnya akan dipadankan terlebih dahulu Jika tidak sepadan, strategi kedua akan dipilih, dan seterusnya.

Penggunaan AffinityLock

Seterusnya mari kita lihat penggunaan khusus Affinity Yang pertama adalah untuk mendapatkan kunci CPU Sebelum JAVA7, kita boleh menulis seperti ini:

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

Dalam After JAVA7, anda boleh menulis seperti ini:

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

Kaedah acquireLock boleh mendapatkan mana-mana cpu yang tersedia untuk benang. Ini adalah kunci berbutir kasar. Jika anda ingin mendapatkan teras berbutir halus, anda boleh menggunakan acquireCore:

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

acquireLock juga mempunyai parameter bind, yang menunjukkan sama ada untuk mengikat benang semasa ke kunci cpu yang diperoleh Jika parameter bind=true, maka semasa Benang akan berjalan pada CPU yang diperolehi dalam acquireLock. Jika parameter bind=false, ini bermakna acquireLock akan terikat pada suatu masa pada masa hadapan.

Kami menyebut AffinityStrategy di atas ini boleh digunakan sebagai parameter acquireLock:

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

Dengan memanggil kaedah acquireLock bagi AffinityLock semasa, urutan semasa boleh diberikan AffinityLock yang berkaitan dengan strategi kunci sebelumnya.

AffinityLock juga menyediakan kaedah dumpLocks untuk melihat status pengikatan semasa CPU dan benang. Mari kita ambil contoh:

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);

Dalam kod di atas, kami mencipta kumpulan utas 4 utas, ThreadFactory yang sepadan ialah AffinityThreadFactory, menamakan kumpulan utas bg dan memperuntukkan 3 AffinityStrategy. Ini bermakna mula-mula memperuntukkan kepada teras yang sama, kemudian kepada soket yang berbeza, dan akhirnya kepada mana-mana CPU yang tersedia.

Semasa proses pelaksanaan tertentu, kami menyerahkan 12 utas, tetapi kumpulan Thread kami hanya mempunyai 4 utas paling banyak. Ia boleh diramalkan bahawa hanya 4 utas dalam hasil yang dikembalikan oleh kaedah AffinityLock.dumpLocks akan terikat kepada. CPU. , mari kita lihat:

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

Seperti yang anda lihat daripada output, CPU0 tidak tersedia. 7 CPU lain tersedia, tetapi hanya mempunyai 4 utas yang terikat padanya, yang sepadan dengan analisis kami sebelum ini.

Seterusnya, kami mengubah suai AffinityStrategy of AffinityThreadFactory seperti berikut:

new AffinityThreadFactory("bg", SAME_CORE)

bermakna benang hanya akan terikat pada teras yang sama, kerana dalam perkakasan semasa, satu teras boleh Ia hanya boleh menyokong pengikatan satu utas, jadi boleh diramalkan bahawa hasil akhir hanya akan terikat pada satu utas Keputusan yang dijalankan adalah seperti berikut:

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);
    }

实时上这种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进行转换。

Atas ialah kandungan terperinci Bagaimana untuk mengikat benang ke CPU tertentu di Jawa?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:yisu.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam