Saya sedang mengerjakan aplikasi Java untuk memecahkan kelas masalah optimasi numerik - masalah pemrograman linier skala besar menjadi lebih tepat. Satu masalah dapat dipecah menjadi sub-masalah yang lebih kecil yang dapat diselesaikan secara paralel. Karena ada lebih banyak submasalah dari inti CPU, saya menggunakan ExecutorService dan mendefinisikan setiap subproblem sebagai Callable yang dikirimkan ke ExecutorService. Memecahkan subproblem membutuhkan memanggil perpustakaan asli - pemecah pemrograman linier dalam kasus ini.
Masalah
Saya dapat menjalankan aplikasi pada Unix dan pada sistem Windows dengan hingga 44 core fisik dan memori 256g, tetapi waktu komputasi pada Windows adalah urutan besarnya lebih tinggi daripada di Linux untuk masalah besar. Windows tidak hanya membutuhkan lebih banyak memori, tetapi pemanfaatan CPU dari waktu ke waktu turun dari 25% di awal menjadi 5% setelah beberapa jam. Berikut ini adalah tangkapan layar dari task manager di Windows:
Pengamatan
- Waktu solusi untuk contoh besar dari keseluruhan masalah berkisar dari berjam-jam dan menghabiskan hingga 32g memori (pada Unix). Waktu solusi untuk subproblem berada dalam kisaran ms.
- Saya tidak menemukan masalah ini pada masalah kecil yang hanya membutuhkan waktu beberapa menit untuk menyelesaikannya.
- Linux menggunakan kedua soket out-of-the-box, sedangkan Windows mengharuskan saya untuk secara eksplisit mengaktifkan interleaving memori di BIOS sehingga aplikasi menggunakan kedua core. Apakah saya melakukan ini tidak berpengaruh pada penurunan pemanfaatan CPU secara keseluruhan dari waktu ke waktu.
- Ketika saya melihat utas di VisualVM semua utas kolam berjalan, tidak ada yang menunggu atau yang lain.
- Menurut VisualVM, 90% waktu CPU dihabiskan untuk panggilan fungsi asli (menyelesaikan program linear kecil)
- Pengumpulan Sampah tidak menjadi masalah karena aplikasi tidak membuat dan mengurangi referensi banyak objek. Juga, sebagian besar memori tampaknya dialokasikan-tumpukan. 4g tumpukan cukup di Linux dan 8g di Windows untuk contoh terbesar.
Apa yang saya coba
- semua jenis argumen JVM, XMS tinggi, metaspace tinggi, flag UseNUMA, GC lain.
- JVM yang berbeda (Hotspot 8, 9, 10, 11).
- perpustakaan asli yang berbeda dari pemecah pemrograman linier yang berbeda (CLP, Xpress, Cplex, Gurobi).
Pertanyaan
- Apa yang mendorong perbedaan kinerja antara Linux dan Windows dari aplikasi Java multi-utas besar yang banyak menggunakan panggilan asli?
- Apakah ada sesuatu yang dapat saya ubah dalam implementasi yang akan membantu Windows, misalnya, haruskah saya menghindari menggunakan ExecutorService yang menerima ribuan Callable dan melakukan apa?
ForkJoinPool
lebih efisien daripada penjadwalan manual.
ForkJoinPool
bukanExecutorService
? Utilisasi CPU 25% benar-benar rendah jika masalah Anda terkait dengan CPU.