Saya sedang menulis beberapa kode di Jawa di mana, di beberapa titik, aliran program ditentukan oleh apakah dua variabel int, "a" dan "b", bukan nol (catatan: a dan b tidak pernah negatif, dan tidak pernah dalam rentang overflow integer).
Saya dapat mengevaluasinya dengan
if (a != 0 && b != 0) { /* Some code */ }
Atau sebagai alternatif
if (a*b != 0) { /* Some code */ }
Karena saya berharap potongan kode itu akan berjalan jutaan kali per kali, saya bertanya-tanya mana yang akan lebih cepat. Saya melakukan percobaan dengan membandingkannya pada array besar yang dihasilkan secara acak, dan saya juga ingin tahu bagaimana sparsity array (fraksi data = 0) akan mempengaruhi hasil:
long time;
final int len = 50000000;
int arbitrary = 0;
int[][] nums = new int[2][len];
for (double fraction = 0 ; fraction <= 0.9 ; fraction += 0.0078125) {
for(int i = 0 ; i < 2 ; i++) {
for(int j = 0 ; j < len ; j++) {
double random = Math.random();
if(random < fraction) nums[i][j] = 0;
else nums[i][j] = (int) (random*15 + 1);
}
}
time = System.currentTimeMillis();
for(int i = 0 ; i < len ; i++) {
if( /*insert nums[0][i]*nums[1][i]!=0 or nums[0][i]!=0 && nums[1][i]!=0*/ ) arbitrary++;
}
System.out.println(System.currentTimeMillis() - time);
}
Dan hasilnya menunjukkan bahwa jika Anda mengharapkan "a" atau "b" sama dengan 0 lebih dari ~ 3% dari waktu, a*b != 0
lebih cepat dari a!=0 && b!=0
:
Saya ingin tahu mengapa. Adakah yang bisa menjelaskan? Apakah itu kompiler atau di tingkat perangkat keras?
Sunting: Karena penasaran ... sekarang saya belajar tentang prediksi cabang, saya bertanya-tanya apa yang akan ditampilkan perbandingan analog untuk OR b adalah nol:
Kami memang melihat efek yang sama dari prediksi cabang seperti yang diharapkan, yang menarik grafik agak membalik sepanjang sumbu X.
Memperbarui
1- Saya menambahkan !(a==0 || b==0)
analisis untuk melihat apa yang terjadi.
2- Saya juga termasuk a != 0 || b != 0
, (a+b) != 0
dan (a|b) != 0
ingin tahu, setelah belajar tentang prediksi cabang. Tapi mereka tidak logis setara dengan ungkapan lain, karena hanya ATAU b perlu non-nol untuk kembali benar, sehingga mereka tidak dimaksudkan untuk dibandingkan untuk efisiensi pengolahan.
3 - Saya juga menambahkan benchmark aktual yang saya gunakan untuk analisis, yang hanya mengulangi variabel int arbitrer.
4 - Beberapa orang menyarankan untuk memasukkan a != 0 & b != 0
sebagai bertentangan dengan a != 0 && b != 0
, dengan prediksi bahwa itu akan berperilaku lebih dekat a*b != 0
karena kami akan menghapus efek prediksi cabang. Saya tidak tahu yang &
bisa digunakan dengan variabel boolean, saya pikir itu hanya digunakan untuk operasi biner dengan bilangan bulat.
Catatan: Dalam konteks yang saya pertimbangkan semua ini, int overflow bukan merupakan masalah, tapi itu jelas merupakan pertimbangan penting dalam konteks umum.
CPU: Intel Core i7-3610QM @ 2.3GHz
Versi Java: 1.8.0_45
Java (TM) SE Runtime Environment (build 1.8.0_45-b14)
Java HotSpot (TM) 64-Bit Server VM (build 25.45-b02, mode campuran)
a != 0 & b != 0
.
a*b!=0
memiliki satu cabang lebih sedikit
(1<<16) * (1<<16) == 0
namun keduanya berbeda dari nol.
a*b
adalah nol jika salah satu dari a
dan b
adalah nol; a|b
adalah nol hanya jika keduanya.
if (!(a == 0 || b == 0))
? Microbenchmarks terkenal tidak bisa diandalkan, ini tidak mungkin benar-benar terukur (~ 3% terdengar seperti margin of error bagi saya).