Baru-baru ini saya menyadari bahwa mendeklarasikan array yang berisi 64 elemen jauh lebih cepat (> 1000 kali lipat) daripada mendeklarasikan tipe array yang sama dengan 65 elemen.
Inilah kode yang saya gunakan untuk menguji ini:
public class Tests{
public static void main(String args[]){
double start = System.nanoTime();
int job = 100000000;//100 million
for(int i = 0; i < job; i++){
double[] test = new double[64];
}
double end = System.nanoTime();
System.out.println("Total runtime = " + (end-start)/1000000 + " ms");
}
}
Ini berjalan di sekitar 6 ms, jika saya mengganti new double[64]
dengan new double[65]
yang dibutuhkan sekitar 7 detik. Masalah ini menjadi lebih parah secara eksponensial jika pekerjaan tersebar di semakin banyak utas, dari situlah masalah saya berasal.
Masalah ini juga terjadi dengan berbagai jenis array seperti int[65]
atau String[65]
. Masalah ini tidak terjadi dengan string besar:String test = "many characters";
tetapi mulai terjadi saat ini diubah menjadiString test = i + "";
Saya bertanya-tanya mengapa hal ini terjadi dan apakah mungkin untuk menghindari masalah ini.
System.nanoTime()
lebih disukai daripadaSystem.currentTimeMillis()
untuk pembandingan.byte
alih - alihdouble
.Jawaban:
Anda mengamati perilaku yang disebabkan oleh pengoptimalan yang dilakukan oleh compiler JIT pada VM Java Anda. Perilaku ini dapat direproduksi dipicu dengan larik skalar hingga 64 elemen, dan tidak dipicu dengan larik yang lebih besar dari 64.
Sebelum masuk ke detailnya, mari kita lihat lebih dekat badan loop:
double[] test = new double[64];
Tubuh tidak berpengaruh (perilaku yang dapat diamati) . Itu berarti tidak ada perbedaan di luar eksekusi program apakah pernyataan ini dijalankan atau tidak. Hal yang sama berlaku untuk seluruh loop. Jadi mungkin saja terjadi, bahwa pengoptimal kode menerjemahkan loop menjadi sesuatu (atau tidak sama sekali) dengan perilaku waktu fungsional dan berbeda yang sama.
Untuk tolok ukur, Anda setidaknya harus mematuhi dua pedoman berikut. Jika Anda melakukannya, perbedaannya akan jauh lebih kecil.
Sekarang mari kita bahas detailnya. Tidak mengherankan jika ada pengoptimalan yang dipicu untuk array skalar tidak lebih dari 64 elemen. Pengoptimalan adalah bagian dari analisis Escape . Ini menempatkan objek kecil dan larik kecil ke tumpukan alih-alih mengalokasikannya di heap - atau bahkan lebih baik mengoptimalkannya sepenuhnya. Anda dapat menemukan beberapa informasi tentang itu dalam artikel berikut oleh Brian Goetz yang ditulis pada tahun 2005:
Pengoptimalan dapat dinonaktifkan dengan opsi baris perintah
-XX:-DoEscapeAnalysis
. Nilai ajaib 64 untuk larik skalar juga dapat diubah pada baris perintah. Jika Anda menjalankan program Anda sebagai berikut, tidak akan ada perbedaan antara array dengan 64 dan 65 elemen:java -XX:EliminateAllocationArraySizeLimit=65 Tests
Karena itu, saya sangat tidak menyarankan menggunakan opsi baris perintah seperti itu. Saya ragu itu membuat perbedaan besar dalam aplikasi yang realistis. Saya hanya akan menggunakannya, jika saya benar-benar yakin akan kebutuhannya - dan tidak berdasarkan hasil dari beberapa tolok ukur palsu.
sumber
Ada beberapa cara untuk membuat perbedaan, berdasarkan ukuran sebuah benda.
Seperti yang dinyatakan nosid, JITC mungkin (kemungkinan besar) mengalokasikan objek "lokal" kecil pada tumpukan, dan batas ukuran untuk larik "kecil" mungkin pada 64 elemen.
Mengalokasikan pada stack secara signifikan lebih cepat daripada mengalokasikan di heap, dan, lebih tepatnya, stack tidak perlu dikumpulkan dari sampah, sehingga overhead GC sangat berkurang. (Dan untuk kasus uji ini, overhead GC kemungkinan 80-90% dari total waktu eksekusi.)
Selanjutnya, setelah nilai dialokasikan tumpukan, JITC dapat melakukan "penghapusan kode mati", menentukan bahwa hasil
new
tidak pernah digunakan di mana pun, dan, setelah memastikan tidak ada efek samping yang akan hilang, hilangkan seluruhnew
operasi, dan kemudian loop (sekarang kosong) itu sendiri.Meskipun JITC tidak melakukan alokasi tumpukan, sangat mungkin objek yang lebih kecil dari ukuran tertentu untuk dialokasikan di heap secara berbeda (misalnya, dari "ruang" yang berbeda) dari objek yang lebih besar. (Namun, biasanya ini tidak akan menghasilkan perbedaan waktu yang begitu dramatis.)
sumber