Quel est le moyen le plus efficace de détecter des nombres pairs en Java?

Quelle serait la manière la plus efficace de déterminer qu’un nombre utilise même Java et pourquoi?

S’agirait-il de modulo, de soustraction ou d’une autre manière à laquelle je n’avais pas pensé?

J’imagine que je pourrais déterminer cela en faisant un simple cours de test – et je le peux – mais cela n’expliquerait vraiment pas pourquoi, n’est-ce pas?

Je ne suis pas en train de faire des réglages de performances loufoques pour un objective ambitieux de traiter autant d’articles plus rapidement. Mais j’étais curieux de savoir si une méthode devrait être préférée à l’autre comme pratique courante. De la même manière, nous n’utiliserions pas & à la place de && , pourquoi utiliser % quand nous pouvons utiliser & ?

Si vous vérifiez l’assemblage généré par le point chaud 7 de ces deux méthodes:

 public static boolean isEvenBit(int i) { return (i & 1) == 0; } public static boolean isEvenMod(int i) { return i % 2 == 0; } 

vous verrez que bien que le mod soit optimisé et qu’il effectue fondamentalement un bit à bit and qu’il comporte quelques instructions supplémentaires car les deux opérations ne sont pas ssortingctement équivalentes *. D’autres machines virtuelles peuvent l’optimiser différemment. L’assemblée est affichée ci-dessous pour référence.

J’ai également exécuté un micro-test qui confirme notre observation: isEventBit est légèrement plus rapide (mais les deux s’exécutent en 2 nanosecondes et n’auront donc probablement pas beaucoup d’incidence sur un programme typique):

 Benchmark Mode Samples Score Error Units capSO16969220.isEvenBit avgt 10 1.869 ± 0.069 ns/op capSO16969220.isEvenMod avgt 10 2.554 ± 0.142 ns/op 

isEvenBit

  # {method} 'isEvenBit' '(I)Z' in 'javaapplication4/Test1' # parm0: rdx = int # [sp+0x20] (sp of caller) 0x00000000026c2580: sub rsp,0x18 0x00000000026c2587: mov QWORD PTR [rsp+0x10],rbp ;*synchronization entry ; - javaapplication4.Test1::isEvenBit@-1 (line 66) 0x00000000026c258c: and edx,0x1 0x00000000026c258f: mov eax,edx 0x00000000026c2591: xor eax,0x1 ;*ireturn ; - javaapplication4.Test1::isEvenBit@11 (line 66) 0x00000000026c2594: add rsp,0x10 0x00000000026c2598: pop rbp 0x00000000026c2599: test DWORD PTR [rip+0xfffffffffdb6da61],eax # 0x0000000000230000 ; {poll_return} 0x00000000026c259f: ret 

isEvenMod

  # {method} 'isEvenMod' '(I)Z' in 'javaapplication4/Test1' # parm0: rdx = int # [sp+0x20] (sp of caller) 0x00000000026c2780: sub rsp,0x18 0x00000000026c2787: mov QWORD PTR [rsp+0x10],rbp ;*synchronization entry ; - javaapplication4.Test1::isEvenMod@-1 (line 63) 0x00000000026c278c: mov r10d,edx 0x00000000026c278f: and r10d,0x1 ;*irem ; - javaapplication4.Test1::isEvenMod@2 (line 63) 0x00000000026c2793: mov r11d,r10d 0x00000000026c2796: neg r11d 0x00000000026c2799: test edx,edx 0x00000000026c279b: cmovl r10d,r11d 0x00000000026c279f: test r10d,r10d 0x00000000026c27a2: setne al 0x00000000026c27a5: movzx eax,al 0x00000000026c27a8: xor eax,0x1 ;*ireturn ; - javaapplication4.Test1::isEvenMod@11 (line 63) 0x00000000026c27ab: add rsp,0x10 0x00000000026c27af: pop rbp 0x00000000026c27b0: test DWORD PTR [rip+0xfffffffffdb6d84a],eax # 0x0000000000230000 ; {poll_return} 0x00000000026c27b6: ret 

* comme indiqué dans les commentaires, % n’est pas vraiment modulo; c’est le rest. Donc (i % 2) != (i & 1) si i < 0 . Les instructions supplémentaires dans le code isEvenMod définissent le signe du résultat sur le signe de i (et le comparent ensuite à zéro, de sorte que l'effort est gaspillé).

Une autre approche consiste à exécuter un micro-repère et à parsingr le temps pris par chaque variante. Voici les résultats:

 Benchmark Mean Units Time vs. baseline baseline 10.330 nsec/op 0.000 bitAnd 12.075 nsec/op 1.745 bitShift 12.309 nsec/op 1.979 modulo 12.309 nsec/op 4.529 

(la ligne de base est une méthode qui renvoie simplement i == 0 )

Conclusion:

  • i & 1 —–> prend environ 1.75ns
  • i << 31 -> prend environ 2.00ns
  • i % 2 -----> prend environ 4.50ns

En d'autres termes, i % 2 est 2x plus lent que i & 1 .

Notes: repère réalisé avec jmh. La ligne de base est élevée car je génère des nombres aléatoires pour m'assurer que la méthode n'est pas optimisée. Les tests sont effectués sur un i7 à 2,8 GHz (soit un cycle = 0,35 ns) avec le hotspot 7.

 if ((i & 1) == 0) { // Even } 

TL; DR La version au niveau des bits et semble être la plus rapide. Référence et échantillon des résultats ci-dessous.


Cela devrait être plus rapide que modulo, car ce ne sont que deux étapes qui peuvent être gérées directement dans le matériel:

 if ((n & 1) == 0) { // even number here } 

Voici un micro-repère qui prouve le sharepoint mon et aasylias:

  // setup int runs = 10; int numbers = 200000000; // 200.000.000 int[] randomNumbers = new int[numbers]; Random random = new Random(); for (int i = 0; i < randomNumbers.length; i++) { randomNumbers[i] = random.nextInt(); } int even = 0; int odd = 0; // bitwiseAnd long andStart = System.currentTimeMillis(); for (int i = 0; i < runs; i++) { for (int number : randomNumbers) { if ((number & 1) == 0) even++; else odd++; } } long andDone = System.currentTimeMillis(); long andDuration = andDone - andStart; System.out.println("Even " + even + ", odd " + odd); // reset variables even = 0; odd = 0; // Modulo long moduloStart = System.currentTimeMillis(); for (int i = 0; i < runs; i++) { for (int number : randomNumbers) { if (number % 2 == 0) even++; else odd++; } } long moduloDone = System.currentTimeMillis(); long moduloDuration = moduloDone - moduloStart; // Done with modulo System.out.println("Even " + even + ", odd " + odd); // reset variables even = 0; odd = 0; // Shift long shiftStart = System.currentTimeMillis(); for (int i = 0; i < runs; i++) { for (int number : randomNumbers) { if ((number << 31) == 0) even++; else odd++; } } long shiftDone = System.currentTimeMillis(); long shiftDuration = shiftDone - shiftStart; // Done with shift System.out.println("Even " + even + ", odd " + odd); System.out.println("Modulo Time " + moduloDuration); System.out.println("Bitwise & Time " + andDuration); System.out.println("Shift Time " + shiftDuration); 

bitwise est toujours un peu plus rapide (même si vous changez de bloc de code avec le bloc modulo). Exemple de sortie:

 Even 999999530, odd 1000000470 Even 999999530, odd 1000000470 Even 999999530, odd 1000000470 Modulo Time 17731 Bitwise & Time 9672 Shift Time 10638