Я пишу какой-то код на Java, где в какой-то момент поток программы определяется тем, являются ли две переменные int "a" и "b" ненулевыми (примечание: a и b никогда не отрицательный и никогда не должен находиться в пределах целых чисел переполнения).
Я могу оценить его с помощью
if (a != 0 && b != 0) { /* Some code */ }
Или, альтернативно,
if (a*b != 0) { /* Some code */ }
Потому что я ожидаю, что часть кода будет работать миллионы раз за запуск, мне было интересно, какой из них будет быстрее. Я сделал эксперимент, сравнив их с огромным беспорядочно сгенерированным массивом, и мне также было любопытно узнать, как разреженность массива (доля данных = 0) повлияет на результаты:
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);
}
И результаты показывают, что если вы ожидаете, что "a" или "b" будет равно 0 больше, чем ~ 3% времени, a*b != 0
будет быстрее, чем a!=0 && b!=0
:
Мне любопытно узнать, почему. Может ли кто-нибудь пролить свет? Является ли это компилятором или находится на уровне оборудования?
Изменить: Из любопытства... теперь, когда я узнал о предсказании ветвей, мне было интересно, что будет показано аналоговое сравнение для OR b, отличное от нуля
Мы видим тот же эффект предсказания ветвления, как и ожидалось, интересно, что график несколько перевернут вдоль оси X.
Update
1- Я добавил !(a==0 || b==0)
к анализу, чтобы узнать, что произойдет.
2- Я также включил a != 0 || b != 0
, (a+b) != 0
и (a|b) != 0
из любопытства, узнав о предсказании ветвления. Но они не логически эквивалентны другим выражениям, потому что только OR b должен быть ненулевым, чтобы возвращать true, поэтому они не предназначены для сравнения эффективности обработки.
3- Я также добавил фактический тест, который я использовал для анализа, который просто выполняет итерацию произвольной переменной int.
4 Некоторые люди предлагали включить a != 0 & b != 0
в отличие от a != 0 && b != 0
с предсказанием того, что он будет вести себя более близко к a*b != 0
, потому что мы удалим эффект предсказания ветвления. Я не знал, что &
можно использовать с булевыми переменными, я думал, что он используется только для двоичных операций с целыми числами.
Примечание. В контексте, что я рассматривал все это, int overflow не является проблемой, но это определенно важное соображение в общих контекстах.
Процессор: Intel Core i7-3610QM @2.3 ГГц
Версия Java: 1.8.0_45
Java (TM) SE Runtime Environment (сборка 1.8.0_45-b14)
Java HotSpot (TM) 64-разрядная серверная VM (сборка 25.45-b02, смешанный режим)