Я пишу якийсь код на 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
:
Мені цікаво знати, чому. Хтось міг пролити світло? Це компілятор чи це на апаратному рівні?
Редагувати: З цікавості ... тепер, коли я дізнався про передбачення галузей, мені було цікаво, що показуватиме аналогове порівняння для АБО b - не нульове:
Ми бачимо такий же ефект прогнозування гілок, як і очікувалося, що цікаво, графік дещо розгорнутий уздовж осі X.
Оновлення
1- Я додав !(a==0 || b==0)
до аналізу, щоб побачити, що відбувається.
2 я включив a != 0 || b != 0
, (a+b) != 0
і (a|b) != 0
з цікавості, після вивчення передбачення розгалужень. Але вони логічно не рівноцінні іншим виразам, тому що лише АБО b має бути ненульовим, щоб повернути істину, тому їх не порівнюють за ефективністю обробки.
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, змішаний режим)
a != 0 & b != 0
.
a*b!=0
має ще одну гілку
(1<<16) * (1<<16) == 0
але обидва відрізняються від нуля.
a*b
дорівнює нулю, якщо один із a
і b
дорівнює нулю; a|b
дорівнює нулю, лише якщо обидва є.
if (!(a == 0 || b == 0))
? Мікро-орієнтири, як відомо, ненадійні, це навряд чи можна виміряти (~ 3% для мене звучить як помилка).