web-dev-qa-db-fra.com

En termes de performances, quelle est la vitesse d'opérateur binaire par rapport au module normal

if(i++ & 1){
}

vs.

if(i % 2){
}

L'utilisation de l'opération Bitwise dans des instructions de flux normales ou conditionnelles telles que for, if etc. permet-elle d'améliorer les performances et serait-il better toujours les utiliser autant que possible?

27
Maven

À moins que vous n'utilisiez un ancien compilateur, il peut déjà gérer seul ce niveau de conversion. C'est-à-dire qu'un compilateur moderne peut et va implémenter i % 2 en utilisant une instruction AND au niveau du bit, à condition qu'il soit logique de le faire sur le CPU cible (ce qui, en toute équité, le fera généralement).

En d’autres termes, ne vous attendez pas à voir aucune différence de performances entre celles-ci, du moins avec un compilateur raisonnablement moderne doté d’un optimiseur raisonnablement compétent. Dans ce cas, reasonably a aussi une définition assez large - même quelques compilateurs vieux de plusieurs décennies peuvent gérer ce type de micro-optimisation sans aucune difficulté.

32
Jerry Coffin

TL; DR _ ​​Écrire d'abord pour la sémantique, optimiser ensuite les points chauds mesurés.

Au niveau de la CPU, le module entier et les divisions font partie des opérations les plus lentes. Mais vous n’écrivez pas au niveau de la CPU, mais au C++, que votre compilateur traduit en une représentation intermédiaire, qui est finalement traduite en Assembly selon le modèle de CPU pour lequel vous compilez.

Dans ce processus, le compilateur appliquera Optimisations de peepphole , parmi lesquelles figure Optimisations de réduction de résistance telles que ( avec l'aimable autorisation de Wikipedia ):

Original Calculation  Replacement Calculation
y = x / 8             y = x >> 3
y = x * 64            y = x << 6
y = x * 2             y = x << 1
y = x * 15            y = (x << 4) - x

Le dernier exemple est peut-être le plus intéressant. Tandis que multiplier ou diviser par deux par deux est facilement converti (manuellement) en opérations par bits, le compilateur est généralement mieux à même de réaliser des transformations encore plus intelligentes auxquelles vous penseriez probablement par vous-même et qui ne sont pas aussi facilement reconnues (à la base). le moins, je ne reconnais pas personnellement immédiatement que (x << 4) - x signifie x * 15).

21
Matthieu M.

Cela dépend évidemment du processeur, mais vous pouvez vous attendre à ce que les opérations au niveau des bits ne prennent jamais plus, et prennent généralement moins de cycles de processeur. En général, les nombres / et % sont très lents, selon les instructions du processeur. Cela dit, avec les pipelines de processeurs modernes ayant une instruction spécifique terminée plus tôt ne signifie pas nécessairement que votre programme s'exécute plus rapidement.

La meilleure pratique consiste à écrire un code compréhensible et maintenable, exprimant la logique qu'il met en œuvre. Il est extrêmement rare que ce type de micro-optimisation fasse une différence tangible. Par conséquent, il ne devrait être utilisé que si le profilage indique un goulet d'étranglement critique et qu'il est prouvé que cela fait une différence significative. De plus, si sur une plate-forme spécifique cela fait une différence significative, votre optimiseur de compilateur peut déjà substituer une opération au niveau du bit quand il peut voir que cela est équivalent.

8
Tony Delroy

Par défaut, vous devez utiliser l'opération qui exprime le mieux le sens voulu, car vous devez optimiser le code lisible. (Aujourd'hui, la ressource la plus rare est le programmeur humain.)

Utilisez donc & si vous extrayez des bits, et % si vous testez la divisibilité, c’est-à-dire si la valeur est pair ou impair.

Pour les valeurs non signées, les deux opérations ont exactement le même effet et votre compilateur doit être suffisamment intelligent pour remplacer la division par l'opération de bit correspondante. Si vous êtes inquiet, vous pouvez vérifier le code d'assemblage qu'il génère.

Malheureusement, la division entière est légèrement irrégulière sur les valeurs signées, car elle arrondit à zéro et le résultat de% change signe en fonction du premier opérande. Les opérations sur les bits, par contre, sont toujours arrondies. Le compilateur ne peut donc pas simplement remplacer la division par une simple opération de bit. Au lieu de cela, il peut soit appeler une routine pour la division entière, soit la remplacer par des opérations sur les bits avec une logique supplémentaire pour traiter l'irrégularité. Cela peut dépendre du niveau d’optimisation et des opérandes constants.

Cette irrégularité à zéro peut même être une mauvaise chose, car c'est une non-linéarité. Par exemple, j'ai récemment eu un cas d'utilisation d'une division sur des valeurs signées d'un ADC, ce qui devait être très rapide sur un Cortex M0 ARM. Dans ce cas, il était préférable de le remplacer par un décalage à droite, à la fois pour la performance et pour se débarrasser de la non-linéarité.

4
starblue

Les opérateurs C ne peuvent pas être comparés de manière significative en termes de "performances". Il n’existe pas d’opérateurs «plus rapides» ou «plus lents» au niveau linguistique. Seul le code machine compilé résultant peut être analysé pour en déterminer les performances. Dans votre exemple spécifique, le code machine résultant sera normalement exactement le même (si nous ignorons le fait que la première condition inclut une incrémentation postfixée pour une raison quelconque), ce qui signifie qu'il n'y aura aucune différence de performances.

2
AnT

Voici le code -O3 optimisé généré par le compilateur (GCC 4.6) pour les deux options:

int i = 34567;
int opt1 = i++ & 1;
int opt2 = i % 2;

Code généré pour opt1:

l     %r1,520(%r11)
nilf  %r1,1
st    %r1,516(%r11)
asi   520(%r11),1

Code généré pour opt2:

l     %r1,520(%r11)
nilf  %r1,2147483649
ltr   %r1,%r1
jhe  .L14
ahi   %r1,-1
oilf  %r1,4294967294
ahi   %r1,1
.L14: st %r1,512(%r11)

Donc, 4 instructions supplémentaires ... qui ne sont rien pour un environnement prod. Ce serait une optimisation prématurée et juste introduire de la complexité

0
user9164692