Laquelle des techniques suivantes est la meilleure option pour diviser un entier par 2 et pourquoi?
Technique 1:
x = x >> 1;
Technique 2:
x = x / 2;
Voici x
un entier.
c++
c
optimization
division
micro-optimization
Abhineet
la source
la source
x
nouveau le résultat , ni l'un ni l'autre n'est approprié de cette façon: il devrait être l'unx >>= 1
ou l' autrex /= 2
, selon ce que vous avez l'intention d'exprimer avec l'opération. Non pas parce qu'il est plus rapide (tout compilateur moderne compilera toutes les variantes équivalentes à un assemblage identique et rapide de toute façon) mais parce qu'il est moins déroutant.x = x >>> 1
. Notez également qu'en fonction de la plate-forme et du compilateur, il peut être tout à fait raisonnable d'optimiser manuellement les divisions et les multiplications à l'aide de décalages. - Penser aux microcontrôleurs, par exemple, sans prise en charge directe ALU pour la multiplication.x /= 2
parce quex >>= 1
ça ressemble trop à une reliure monadique;)x = x / 2
au lieu dex /= 2
. Préférence subjective peut-être :)⬜=
combinaisons, celles-ci doivent être utilisées chaque fois que cela est possible. Il supprime le bruit et met l'accent sur le fait qu'ilx
est modifié , tandis que l'=
opérateur général suggère plutôt qu'il prenne une valeur complètement nouvelle indépendante de l'ancienne. - Toujours en évitant les opérateurs combinés (pour qu'il soit lisible que quelqu'un qui ne connaît que les opérateurs mathématiques) peut avoir son point aussi, mais vous auriez besoin de renoncer à la très utile++
,--
,+=
aussi.Réponses:
Utilisez l'opération qui décrit le mieux ce que vous essayez de faire.
Notez qu'ils ne sont pas exactement équivalents. Ils peuvent donner des résultats différents pour les entiers négatifs. Par exemple:
(ideone)
la source
%
et/
doivent être cohérents pour les opérandes positifs et négatifs, ce qui(a/b)*b+(a%b)==a
est vrai quels que soient les signes dea
etb
. Habituellement, l'auteur fera des choix qui obtiendront les meilleures performances possibles du CPU.La première ressemble-t-elle à une division? Non. Si vous voulez diviser, utilisez
x / 2
. Le compilateur peut l'optimiser pour utiliser le bit-shift si possible (c'est ce qu'on appelle la réduction de la force), ce qui en fait une micro-optimisation inutile si vous le faites vous-même.la source
Pour empiler: il y a tellement de raisons de favoriser l'utilisation
x = x / 2;
Voici quelques-unes:il exprime plus clairement votre intention (en supposant que vous n'avez pas affaire à des bits de registre de torsion de bits ou à quelque chose)
le compilateur le réduira de toute façon à une opération de décalage
même si le compilateur ne l'a pas réduit et a choisi une opération plus lente que le changement, la probabilité que cela finisse par affecter les performances de votre programme de manière mesurable est elle-même extrêmement faible (et si elle l'affecte de manière mesurable, alors vous avez un réel raison d'utiliser un quart de travail)
si la division va faire partie d'une expression plus large, il est plus probable que vous ayez la priorité si vous utilisez l'opérateur de division:
l'arithmétique signée pourrait compliquer les choses encore plus que le problème de priorité mentionné ci-dessus
pour réitérer - le compilateur le fera déjà de toute façon. En fait, il convertira la division par une constante en une série de décalages, s'ajoute et se multiplie pour toutes sortes de nombres, pas seulement des puissances de deux. Voir cette question pour des liens vers encore plus d'informations à ce sujet.
En bref, vous n'achetez rien en codant un décalage lorsque vous voulez vraiment multiplier ou diviser, sauf peut-être une possibilité accrue d'introduire un bug. Cela fait toute une vie que les compilateurs n'étaient pas assez intelligents pour optimiser ce genre de chose en un quart de travail le cas échéant.
la source
a/b/c*d
(oùa..d
dénoté des variables numériques) au lieu de beaucoup plus lisible(a*d)/(b*c)
.a*d
oub*c
qui produirait un débordement, la forme moins lisible n'est pas équivalente et a un avantage évident. PS Je suis d'accord que les parenthèses sont votre meilleur ami.a/b/c*d
du code R - dans un contexte où un débordement signifierait que quelque chose n'allait vraiment pas avec les données - et non, par exemple, dans un bloc de code C à performances critiques).x=x/2;
est seulement "plus clair" quex>>=1
s'ilx
ne sera jamais un nombre négatif impair ou que l'on ne se soucie pas d'erreurs ponctuelles. Sinonx=x/2;
etx>>=1;
ont des significations différentes. Si ce dont on a besoin est la valeur calculée parx>>=1
, je considérerais cela comme plus clair quex = (x & ~1)/2
oux = (x < 0) ? (x-1)/2 : x/2
, ou toute autre formulation à laquelle je peux penser d'utiliser la division par deux. De même, si l'on a besoin de la valeur calculée parx/=2
, c'est plus clair que((x + ((unsigned)x>>31)>>1)
.Cela dépend de ce que vous entendez par le meilleur .
Si vous voulez que vos collègues vous haïssent ou rendent votre code difficile à lire, j'irais certainement avec la première option.
Si vous voulez diviser un nombre par 2, optez pour le second.
Les deux ne sont pas équivalents, ils ne se comportent pas de la même façon si le nombre est négatif ou dans des expressions plus grandes - le décalage de bits a une priorité inférieure à
+
ou-
, la division a une priorité plus élevée.Vous devez écrire votre code pour exprimer son intention. Si les performances vous préoccupent, ne vous inquiétez pas, l'optimiseur fait du bon travail avec ce type de micro-optimisations.
la source
Utilisez simplement divide (
/
), en supposant que c'est plus clair. Le compilateur optimisera en conséquence.la source
ASSUME(x >= 0); x /= 2;
plusx >>= 1;
, mais qui est encore un point important de mettre en place.Je suis d'accord avec d'autres réponses que vous devriez privilégier
x / 2
car son intention est plus claire et le compilateur devrait l'optimiser pour vous.Cependant, une autre raison de préférer
x / 2
plusx >> 1
est que le comportement de>>
dépend de la mise en œuvre six
est un signéint
et est négative.De la section 6.5.7, puce 5 de la norme ISO C99:
la source
x>>scalepower
les nombres négatifs sera précisément ce qui est nécessaire lors de la division d'une valeur par une puissance de deux à des fins telles que le rendu d'écran, tandis que l'utilisationx/scalefactor
sera erronée à moins que l'on applique des corrections aux valeurs négatives.x / 2
est plus clair etx >> 1
n'est pas beaucoup plus rapide (selon un micro-benchmark, environ 30% plus rapide pour une JVM Java). Comme d'autres l'ont noté, pour les nombres négatifs, l'arrondi est légèrement différent, vous devez donc en tenir compte lorsque vous souhaitez traiter des nombres négatifs. Certains compilateurs peuvent se convertir automatiquementx / 2
enx >> 1
s'ils savent que le nombre ne peut pas être négatif (même pensé que je ne pouvais pas vérifier).Même
x / 2
ne peut pas utiliser l'instruction CPU de division (lente), car certains raccourcis sont possibles , mais il est toujours plus lent quex >> 1
.(Ceci est une question C / C ++, d' autres langages de programmation ont plusieurs opérateurs. Pour Java il y a aussi le décalage droit non signé,
x >>> 1
qui est encore différente. Il permet de calculer correctement la valeur moyenne (moyenne) de deux valeurs, de sorte que la(a + b) >>> 1
volonté renvoie la valeur moyenne même pour les très grandes valeurs dea
etb
. Ceci est requis par exemple pour la recherche binaire si les indices du tableau peuvent devenir très grands. Il y avait un bogue dans de nombreuses versions de la recherche binaire , car elles servaient(a + b) / 2
à calculer la moyenne. ne fonctionne pas correctement. La bonne solution est d’utiliser à la(a + b) >>> 1
place.)la source
x/2
àx>>1
dans les cas oùx
peut être négatif. Si ce que l'on veut, c'est la valeur quix>>1
serait calculée, ce sera presque certainement plus rapide que toute expression impliquantx/2
qui calcule la même valeur.x/2
àx>>1
s'il connaît la valeur est négative. Je vais essayer de mettre à jour ma réponse.div
instruction, en convertissantx/2
en(x + (x<0?1:0)) >> 1
(où >> est un décalage arithmétique à droite, qui se décale en bits de signe). Cela prend 4 instructions: copier la valeur, shr (pour obtenir juste le bit de signe dans un reg), ajouter, sar. goo.gl/4F8Ms4Knuth a déclaré:
Je suggère donc d'utiliser
x /= 2;
De cette façon, le code est facile à comprendre et je pense également que l'optimisation de cette opération sous cette forme, ne signifie pas une grande différence pour le processeur.
la source
(n+8)>>4
marche bien. Pouvez-vous proposer une approche aussi claire ou aussi efficace sans utiliser un décalage à droite signé?Jetez un œil à la sortie du compilateur pour vous aider à décider. J'ai exécuté ce test sur x86-64 avec
gcc (GCC) 4.2.1 20070719 [FreeBSD]
Voir également les sorties du compilateur en ligne sur godbolt .
Ce que vous voyez, c'est que le compilateur utilise une
sarl
instruction (décalage vers la droite arithmétique) dans les deux cas, il reconnaît donc la similitude entre les deux expressions. Si vous utilisez la division, le compilateur doit également ajuster les nombres négatifs. Pour ce faire, il décale le bit de signe vers le bit de poids faible et l'ajoute au résultat. Cela résout le problème du coup par coup lors du déplacement de nombres négatifs, par rapport à ce que ferait une division.Étant donné que le cas de division fait 2 changements, alors que le cas de changement explicite n'en fait qu'un, nous pouvons maintenant expliquer ici certaines des différences de performances mesurées par d'autres réponses.
Code C avec sortie d'assemblage:
Pour diviser, votre entrée serait
et cela compile pour
de même pour le décalage
avec sortie:
la source
d
. Un tel partitionnement est utile à de nombreuses fins. Même si l'on préfère avoir le point d'arrêt ailleurs qu'entre 0 et -1, l'ajout d'un décalage le déplacera. Une division entière qui satisfait le deuxième axiome divisera la droite numérique en régions qui sont principalement de tailled
, mais dont l'une est de taille2*d-1
. Pas exactement des divisions "égales". Pouvez-vous suggérer quand la partition Oddball est réellement utile?sar
). goo.gl/KRgIkb . Cette publication sur la liste de diffusion ( gcc.gnu.org/ml/gcc/2000-04/msg00152.html ) confirme que gcc utilise historiquement des décalages arithmétiques pour les entrées signées, il est donc très peu probable que FreeBSD gcc 4.2.1 ait utilisé un décalage non signé. J'ai mis à jour votre message pour corriger cela et le premier paragraphe disant que les deux utilisaient shr, alors que c'est en fait SAR qu'ils utilisent tous les deux. Le SHR est la façon dont il extrait le bit de signe pour le/
boîtier. Également inclus un lien Godbolt.Juste une note ajoutée -
x * = 0,5 sera souvent plus rapide dans certains langages basés sur des machines virtuelles - notamment actionscript, car la variable ne devra pas être vérifiée pour la division par 0.
la source
eval
) faisaient en sorte que cela se reproduise à chaque fois. Quoi qu'il en soit, oui, c'est un très mauvais test, car c'est une optimisation très stupide.Utilisez
x = x / 2;
OR,x /= 2;
car il est possible qu'un nouveau programmeur y travaille à l'avenir. Il lui sera donc plus facile de savoir ce qui se passe dans la ligne de code. Tout le monde peut ne pas être au courant de telles optimisations.la source
Je dis dans le but de programmer des compétitions. Généralement, ils ont de très grandes entrées où la division par 2 a lieu plusieurs fois et il est connu que l'entrée est positive ou négative.
x >> 1 sera meilleur que x / 2. J'ai vérifié sur ideone.com en exécutant un programme où plus de 10 ^ 10 divisions par 2 opérations ont eu lieu. x / 2 a pris près de 5,5 s tandis que x >> 1 a pris près de 2,6 s pour le même programme.
la source
x/2
pourx>>1
. Pour les valeurs signées, presque toutes les implémentations définissentx>>1
une signification équivalentex/2
mais qui peut être calculée plus rapidement lorsqu'ellex
est positive, et qui est utilement différente dex/2
lorsqu'ellex
est négative.Je dirais qu'il y a plusieurs choses à considérer.
Bitshift devrait être plus rapide, car aucun calcul spécial n'est vraiment nécessaire pour décaler les bits, mais comme indiqué, il existe des problèmes potentiels avec les nombres négatifs. Si vous êtes assuré d'avoir des nombres positifs et que vous recherchez la vitesse, je recommanderais le bitshift.
L'opérateur de division est très facile à lire pour les humains. Donc, si vous recherchez la lisibilité du code, vous pouvez l'utiliser. Notez que le domaine de l'optimisation du compilateur a parcouru un long chemin, donc rendre le code facile à lire et à comprendre est une bonne pratique.
Si vous recherchez des performances pures, je recommanderais de créer des tests qui pourraient effectuer les opérations des millions de fois. Échantillonnez l'exécution plusieurs fois (la taille de votre échantillon) pour déterminer laquelle est statistiquement la meilleure avec votre système d'exploitation / matériel / compilateur / code.
la source
>>
correspond et ne correspond pas à ce qui le/
fait.En ce qui concerne le CPU, les opérations de décalage de bits sont plus rapides que les opérations de division. Cependant, le compilateur le sait et optimisera de manière appropriée dans la mesure du possible, afin que vous puissiez coder de la manière la plus logique et la plus simple possible en sachant que votre code fonctionne efficacement. Mais rappelez-vous qu'un
unsigned int
peut (dans certains cas) être mieux optimisé qu'unint
pour des raisons précédemment indiquées. Si vous n'avez pas besoin d'arithmatique signée, n'incluez pas le bit de signe.la source
x = x / 2; est le code approprié à utiliser .. mais une opération dépend de votre propre programme de la façon dont la sortie que vous souhaitez produire.
la source
Rendez vos intentions plus claires ... par exemple, si vous voulez diviser, utilisez x / 2 et laissez le compilateur l'optimiser pour déplacer l'opérateur (ou autre chose).
Les processeurs d'aujourd'hui ne laisseront pas ces optimisations avoir un impact sur les performances de vos programmes.
la source
La réponse à cela dépendra de l'environnement dans lequel vous travaillez.
x /= 2
enx >>= 1
, la présence d'un symbole de division soulèvera plus de sourcils dans cet environnement que en utilisant un décalage pour effectuer une division.x >>= 1
un commentaire expliquant son raisonnement est probablement préférable pour des raisons de clarté.x /= 2
. Mieux vaut sauver le prochain programmeur qui regarde votre code la double prise de 10 secondes sur votre opération de décalage que de prouver inutilement que vous saviez que le décalage était plus efficace sans optimisation du compilateur.Tout cela suppose des entiers non signés. Le changement simple n'est probablement pas ce que vous souhaitez signer. En outre, DanielH soulève un bon point sur l'utilisation
x *= 0.5
de certains langages comme ActionScript.la source
mod 2, testez pour = 1. ne sais pas la syntaxe en c. mais cela peut être le plus rapide.
la source
généralement le décalage à droite divise:
ceci est parfois utilisé pour accélérer les programmes au détriment de la clarté. Je ne pense pas que tu devrais le faire. Le compilateur est suffisamment intelligent pour effectuer automatiquement l'accélération. Cela signifie que vous ne gagnez rien au détriment de la clarté .
Jetez un œil à cette page de la programmation C ++ pratique.
la source
(x+128)>>8
, calculerait pour des valeurs quix
ne sont pas proches du maximum, comment pourrait-on le faire de manière concise sans décalage? Notez que(x+128)/256
cela ne fonctionnera pas. Connaissez-vous une belle expression qui le fera?Évidemment, si vous écrivez votre code pour le prochain gars qui le lira, optez pour la clarté de "x / 2".
Cependant, si la vitesse est votre objectif, essayez-le dans les deux sens et chronométrez les résultats. Il y a quelques mois, j'ai travaillé sur une routine de convolution bitmap qui impliquait de parcourir un tableau d'entiers et de diviser chaque élément par 2. J'ai fait toutes sortes de choses pour l'optimiser, y compris l'ancienne astuce consistant à remplacer "x >> 1" par "x / 2 ".
Quand j'ai effectivement chronométré les deux sens, j'ai découvert à ma grande surprise que x / 2 était plus rapide que x >> 1
Cela utilisait Microsoft VS2008 C ++ avec les optimisations par défaut activées.
la source
En termes de performances. Les opérations de décalage du processeur sont nettement plus rapides que les codes d'opération de division. Donc, en divisant par deux ou en multipliant par 2, etc., tous bénéficient d'opérations de quart de travail.
Quant à l'apparence. En tant qu'ingénieurs, quand sommes-nous devenus si attachés aux cosmétiques que même les belles femmes ne les utilisent pas! :)
la source
X / Y est correct ... et l'opérateur de décalage ">>" ... si nous voulons que deux divisent un entier, nous pouvons utiliser l'opérateur de dividende (/). L'opérateur shift est utilisé pour décaler les bits.
x = x / 2; x / = 2; nous pouvons utiliser comme ça ..
la source
Alors que x >> 1 est plus rapide que x / 2, l'utilisation appropriée de >> lorsqu'il s'agit de valeurs négatives est un peu plus compliquée. Cela nécessite quelque chose de semblable au suivant:
la source