Bahkan, orang yang menulis Java tampaknya tidak ada hubungannya dengan CPU. Paling -paling, ini ada hubungannya dengan cara menjalankan CPU dan cara mengatur jumlah utas yang kami sebutkan sebelumnya. Namun, algoritma itu hanyalah referensi. Banyak skenario yang berbeda membutuhkan cara praktis untuk menyelesaikannya. Selain itu, setelah menjalankan CPU, kami juga akan mempertimbangkan bagaimana membuat CPU tidak begitu penuh. Haha, manusia, itu saja. Haha, oke, artikel ini tentang hal -hal lain. Mungkin Anda hampir tidak menulis kode di Java. Perhatikan CPU, karena memuaskan bisnis adalah hal penting pertama. Jika Anda ingin mencapai tingkat kerangka kerja dan menyediakan kerangka kerja dengan banyak cache data bersama, harus ada banyak masalah permintaan data di tengah. Tentu saja, Java menyediakan banyak kelas paket bersamaan, dan Anda dapat menggunakannya, tetapi bagaimana hal itu dilakukan secara internal, Anda harus memahami detail untuk menggunakannya dengan lebih baik, jika tidak lebih baik tidak menggunakannya. Artikel ini mungkin tidak menjelaskan konten ini sebagai fokus, karena seperti pesta judul: kami ingin berbicara tentang CPU, haha.
Hal yang sama dikatakan, tampaknya Java tidak ada hubungannya dengan CPU, jadi mari kita bicara tentang apa yang sedang terjadi sekarang;
1. Saat menemukan elemen bersama, ide pertama kami adalah memastikan operasi baca yang konsisten melalui volatile, yaitu, visibilitas absolut. Visibilitas yang disebut berarti bahwa setiap kali Anda ingin menggunakan data ini, CPU tidak akan menggunakan konten cache apa pun dan akan mengambil data dari memori. Proses ini masih berlaku untuk beberapa CPU, yang berarti bahwa CPU dan memori disinkronkan saat ini. CPU akan mengeluarkan instruksi perakitan yang mirip dengan kunci addl 0 seperti bus, +0 tetapi tidak akan melakukan apa pun relatif terhadap apa pun. Namun, setelah instruksi selesai, operasi selanjutnya tidak akan lagi mempengaruhi akses utas lain dari elemen ini, yang merupakan visibilitas absolut yang dapat dicapai, tetapi tidak dapat menerapkan operasi yang konsisten. Dengan kata lain, apa yang tidak dapat dicapai oleh volatile adalah konsistensi operasi seperti i ++ (konkurensi di bawah beberapa utas), karena operasi i ++ terurai menjadi:
int tmp = i; tmp = tmp + 1; i = tmp;
Tiga langkah ini selesai. Dari titik ini, Anda juga dapat melihat mengapa i ++ dapat melakukan hal -hal lain terlebih dahulu dan kemudian menambahkan 1 pada dirinya sendiri, karena itu ditetapkan nilainya ke variabel lain.
2. Jika kita ingin menggunakan konsistensi konkurensi multi-utas, kita perlu menggunakan mekanisme kunci. Saat ini, hal -hal seperti atom* pada dasarnya dapat memenuhi persyaratan ini. Banyak metode kelas yang tidak aman disediakan secara internal. Dengan terus membandingkan data visibilitas absolut, kami dapat memastikan bahwa data yang diperoleh terbaru; Selanjutnya kita akan terus membicarakan masalah CPU lainnya.
3. Di masa lalu, kami tidak dapat menjalankan CPU untuk mengisinya, tetapi kami tidak puas tidak peduli bagaimana kami mulai mengabaikan keterlambatan antara memori dan CPU. Karena kami menyebutkannya hari ini, kami akan berbicara secara singkat tentang keterlambatan tersebut. Secara umum, CPU saat ini memiliki cache tiga tingkat, dan penundaannya berbeda dalam usia yang berbeda, sehingga jumlah spesifik hanya bisa secara kasar. CPU saat ini umumnya memiliki keterlambatan 1-2NS, cache tingkat kedua umumnya beberapa NS hingga sekitar sepuluh ns, dan cache tingkat ketiga umumnya antara 30ns dan 50ns, dan akses memori umumnya akan mencapai 70ns atau bahkan lebih (komputer berkembang sangat cepat, dan nilai ini hanya untuk data pada beberapa CPU, untuk kisaran rujukan); Meskipun penundaan ini sangat kecil, semuanya berada di tingkat nanodetik, Anda akan menemukan bahwa ketika program Anda dibagi menjadi operasi instruksi, akan ada banyak interaksi CPU. Jika keterlambatan setiap interaksi sangat besar, kinerja sistem akan berubah saat ini;
4. Kembali ke volatile yang disebutkan sekarang. Setiap kali mendapat data dari memori, ia meninggalkan cache. Tentu saja, jika menjadi lebih lambat dalam beberapa operasi utusan tunggal, itu akan menjadi lebih lambat. Terkadang kita harus melakukan ini. Bahkan operasi membaca dan menulis memerlukan konsistensi, dan bahkan seluruh blok data disinkronkan. Kita hanya dapat mengurangi granularitas kunci sampai batas tertentu, tetapi kita tidak dapat memiliki kunci sama sekali. Bahkan tingkat CPU itu sendiri akan memiliki batasan tingkat instruksi.
5. Operasi atom di tingkat CPU umumnya disebut hambatan, dengan hambatan baca, hambatan tulis, dll. Mereka umumnya dipicu oleh satu titik. Ketika beberapa instruksi dari program dikirim ke CPU, beberapa instruksi mungkin tidak dieksekusi dalam urutan program, dan beberapa harus dieksekusi dalam urutan program, selama mereka dapat dijamin konsisten dalam urutan akhir program. Dalam hal penyortiran, JIT akan berubah selama runtime, dan tingkat instruksi CPU juga akan berubah. Alasan utama adalah untuk mengoptimalkan instruksi runtime untuk membuat program berjalan lebih cepat.
6. Level CPU akan mengoperasikan garis cache pada memori. Garis cache yang disebut akan membaca sepotong memori terus menerus, yang umumnya terkait dengan model dan arsitektur CPU. Saat ini, banyak CPU umumnya akan membaca memori kontinu setiap kali, dan yang awal akan memiliki 32byte, sehingga akan lebih cepat ketika melintasi beberapa array (sangat lambat berdasarkan traversal kolom), tetapi ini tidak sepenuhnya benar. Berikut ini akan membandingkan beberapa situasi yang berlawanan.
7. Jika CPU mengubah data, kita harus berbicara tentang keadaan CPU yang memodifikasi data. Jika semua data dibaca, itu dapat dibaca secara paralel dengan beberapa utas di bawah beberapa CPU. Saat menulis operasi pada blok data, itu berbeda. Blok data akan memiliki status eksklusif, dimodifikasi, tidak validasi dan lainnya, dan data secara alami akan gagal setelah modifikasi. Ketika beberapa utas memodifikasi blok data yang sama di bawah beberapa CPU, Copy Data Bus (QPI) antara CPU akan terjadi. Tentu saja, jika kami memodifikasinya ke data yang sama, kami tidak punya pilihan, tetapi ketika kami kembali ke garis cache pada titik 6, masalahnya lebih merepotkan. Jika data berada pada array yang sama, dan elemen-elemen dalam array akan di-cache ke CPU pada saat yang sama, qPI multi-thread akan sangat sering. Terkadang masalah ini akan terjadi bahkan jika objek yang dirakit pada array dirakit, seperti:
class inputInteger {private int value; inputIntEnteger publik (int i) {this.value = i;}} inputInteger [] integers = inputInteger baru [ukuran]; untuk (int i = 0; i <size; i ++) {integer [i] = new inputIntEnteGer (i);} Pada saat ini, Anda dapat melihat bahwa segala sesuatu dalam bilangan bulat adalah objek, dan hanya ada referensi ke objek pada array, tetapi pengaturan objek secara teoritis independen dan tidak akan disimpan terus menerus. Namun, ketika Java mengalokasikan memori objek, itu sering dialokasikan terus menerus di daerah Eden. Saat berada di loop untuk, jika tidak ada utas lain yang diakses, objek -objek ini akan disimpan bersama. Bahkan jika mereka GC ke daerah lama, kemungkinan besar akan disatukan. Oleh karena itu, cara untuk memodifikasi seluruh array dengan mengandalkan objek sederhana untuk menyelesaikan garis cache tampaknya tidak dapat diandalkan, karena int itu adalah 4 byte. Jika dalam mode 64, ukuran ini adalah 24 byte (4bytes diisi), dan kompresi pointer adalah 16 byte; Artinya, CPU dapat mencocokkan 3-4 objek setiap kali. Cara membuat cache CPU, tetapi tidak mempengaruhi QPI sistem. Jangan berpikir untuk menyelesaikannya dengan memisahkan objek, karena proses penyalinan memori proses GC kemungkinan akan disalin bersama. Cara terbaik adalah mengisinya. Meskipun sedikit limbah memori, ini adalah metode yang paling dapat diandalkan, yaitu untuk mengisi objek menjadi 64 byte. Jika kompresi pointer tidak diaktifkan, ada 24bytes, dan ada 40 byte saat ini. Anda hanya perlu menambahkan 5 long di dalam objek.
class inputInteger {nilai int publik; private long a1, a2, a3, a4, a5;} Haha, metode ini sangat pedesaan, tetapi bekerja dengan sangat baik. Kadang -kadang, ketika JVM dikompilasi, ia menemukan bahwa parameter ini belum dilakukan, sehingga dibunuh secara langsung untuk Anda. Optimalisasi tidak valid. Metode ditambah metode ini adalah dengan hanya mengoperasikan 5 parameter ini dalam badan metode (menggunakan semuanya), tetapi metode ini tidak akan pernah menyebutnya.
8. Di tingkat CPU, kadang -kadang tidak mungkin melakukan hal pertama yang harus dilakukan. Itu adalah raja. Dalam pengoperasian AtomicIntegerfieldUpdater, jika Anda menelepon Getanteset (benar) dalam satu utas, Anda akan menemukan bahwa itu berjalan cukup cepat, dan mulai melambat di bawah CPU multi-core. Mengapa dikatakan dengan jelas di atas? Karena GetAndset dimodifikasi dan dibandingkan, dan kemudian mengubahnya terlebih dahulu, QPI akan sangat tinggi, jadi saat ini, lebih baik melakukan operasi terlebih dahulu dan kemudian memodifikasinya; Dan itu juga cara yang baik untuk mendapatkannya sekali. Jika tidak dapat diperoleh, berikan dan biarkan utas lain melakukan hal -hal lain;
9. Kadang -kadang, untuk menyelesaikan masalah beberapa CPU sibuk dan tidak sibuk, akan ada banyak algoritma untuk dipecahkan. Misalnya, NUMA adalah salah satu solusi. Namun, tidak peduli arsitektur mana yang lebih berguna dalam skenario tertentu, itu mungkin tidak efektif untuk semua skenario. Ada mekanisme kunci antrian untuk menyelesaikan manajemen negara CPU, tetapi ini juga memiliki masalah garis cache, karena keadaan sering berubah, dan inti dari berbagai aplikasi juga akan menghasilkan beberapa algoritma untuk dilakukan untuk bekerja sama dengan CPU, sehingga CPU dapat digunakan secara lebih efektif, seperti anu CLH.
Ada banyak detail tentang ini, seperti superposisi loop variabel biasa, tipe volatile, dan seri atom*, yang sama sekali berbeda; Loop array multi-dimensi, looping dalam urutan ke belakang di garis lintang yang berbeda, dan ada banyak detail, dan saya mengerti mengapa ada inspirasi dalam proses optimasi yang sebenarnya; Detail kunci terlalu tipis dan pusing, dan di tingkat bawah sistem, selalu ada beberapa operasi atom yang ringan. Tidak peduli siapa yang mengatakan bahwa kodenya tidak memerlukan penguncian, yang terbaik bisa sesederhana CPU hanya dapat menjalankan satu instruksi pada setiap saat. CPU multi-core juga akan memiliki area bersama untuk mengontrol beberapa konten di tingkat bus, termasuk level baca, tingkat tulis, tingkat memori, dll. Dalam skenario yang berbeda, granularitas kunci berkurang sebanyak mungkin. Kinerja sistem terbukti dengan sendirinya, dan itu adalah hasil yang normal.