J'écris du code en Java où, à un moment donné, le flux du programme est déterminé par le fait que deux variables int, "a" et "b", sont non nulles (note: a et b ne sont jamais négatives, et jamais dans la plage de dépassement d'entier).
Je peux l'évaluer avec
if (a != 0 && b != 0) { /* Some code */ }
Ou bien
if (a*b != 0) { /* Some code */ }
Parce que je m'attends à ce que ce morceau de code s'exécute des millions de fois par exécution, je me demandais lequel serait plus rapide. J'ai fait l'expérience en les comparant sur un énorme tableau généré de manière aléatoire, et j'étais également curieux de voir comment la rareté du tableau (fraction de données = 0) affecterait les résultats:
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);
}
Et les résultats montrent que si vous vous attendez à ce que "a" ou "b" soit égal à 0 plus de ~ 3% du temps, a*b != 0
c'est plus rapide que a!=0 && b!=0
:
Je suis curieux de savoir pourquoi. Quelqu'un pourrait-il faire la lumière? Est-ce le compilateur ou est-ce au niveau matériel?
Edit: Par curiosité ... maintenant que j'ai appris la prédiction de branche, je me demandais ce que la comparaison analogique montrerait pour un OR b est non nul:
Nous voyons le même effet de prédiction de branche que prévu, ce qui est intéressant, le graphique est quelque peu inversé le long de l'axe X.
Mise à jour
1- J'ai ajouté !(a==0 || b==0)
à l'analyse pour voir ce qui se passe.
2- J'ai également inclus a != 0 || b != 0
, (a+b) != 0
et (a|b) != 0
par curiosité, après avoir appris la prédiction de branche. Mais elles ne sont pas logiquement équivalentes aux autres expressions, car seul un OR b doit être différent de zéro pour retourner vrai, donc elles ne sont pas censées être comparées pour l'efficacité du traitement.
3- J'ai également ajouté le benchmark réel que j'ai utilisé pour l'analyse, qui est juste une itération d'une variable int arbitraire.
4- Certaines personnes ont suggéré d'inclure a != 0 & b != 0
par opposition à a != 0 && b != 0
, avec la prédiction qu'elle se comporterait plus étroitement a*b != 0
car nous supprimerions l'effet de prédiction de branche. Je ne savais pas que cela &
pouvait être utilisé avec des variables booléennes, je pensais que c'était seulement utilisé pour des opérations binaires avec des entiers.
Remarque: Dans le contexte que je considérais tout cela, le débordement int n'est pas un problème, mais c'est certainement une considération importante dans des contextes généraux.
Processeur: Intel Core i7-3610QM @ 2,3 GHz
Version Java: 1.8.0_45
Java (TM) SE Runtime Environment (build 1.8.0_45-b14)
VM Server HotSpot (TM) 64 bits Server (build 25.45-b02, mode mixte)
a != 0 & b != 0
.
a*b!=0
a une branche en moins
(1<<16) * (1<<16) == 0
pourtant les deux sont différents de zéro.
a*b
est nul si l' un de a
et b
est nul; a|b
est nul uniquement si les deux le sont.
if (!(a == 0 || b == 0))
? Les microbenchmarks sont notoirement peu fiables, il est peu probable qu'ils soient vraiment mesurables (~ 3% me semble une marge d'erreur).