11
votes

Doubler un nombre - SHIFT à gauche vs multiplication

Quelles sont les différences entre

int size = length << 1; // (2)


1 commentaires

Je ne suis pas sûr, mais je soppose, les compilateurs modernes sont en mesure d'optimiser les deux déclarations à (presque) les mêmes.


10 Réponses :


2
votes

Cela ressemble à des optimisations prématurées. Le plus pour juste faire la longueur * 2 est que le compilateur optimise certainement cela et il est plus facile de maintenir.


0 commentaires

26
votes

Et voici la 3ème option: xxx

et cela doit être la meilleure option. Comme il est lisible et facile de comprendre ce que vous voulez faire. En ce qui concerne la performance, les compilateurs génèrent un code assez optimisé. Il n'est donc pas nécessaire de vous inquiéter d'une opération aussi simple. Si vous avez des préoccupations concernant le débordement, vous pouvez utiliser coché bloc.

modifier Comme mentionné par beaucoup d'autres, utilisez une variable significative au lieu de 2 ici.


10 commentaires

Je pense que longueur * 2 'est en fait pire que la première option. Votre proposition décrit l'intention du code moins. Il y a une autre option que je pense est meilleure. Il devrait utiliser des constantes nommées au lieu de 200l` et `100l '. Cela fait un meilleur travail pour décrire l'intention du code.


Et quel est l'avantage de la 4ème option? S'il y a une préoccupation pour le bloc vérifié sur le débordement suffira.


@Incognito: Je suis désolé, mon commentaire n'était pas très clair. Je ne crois pas que la performance n'est pas un problème ici, car le compilateur optimisera. Votre option et votre mine. Vous commencez sur la lisibilité et je conviens que la lisibilité est très importante, mais ne pensez pas que longueur * 2 est le plus lisible. Il y a probablement une raison de faire ce calcul. Par conséquent, je pense que l'utilisation de constantes nommées est meilleure pour la performance. d'ailleurs. J'étais un peu trop impatient de vous voter. Pouvez-vous modifier votre commentaire. C'est le seul moyen pour moi d'annuler cette vote en baisse.


"Il y a probablement une raison", c'est l'exemple ultime de code illisible.


@Steven: Pas de soucis :) J'ai édité la réponse. Quoi qu'il en soit, vous avez le bon point sur les nombres magiques.


@Everyone: Si quelqu'un ne peut pas comprendre ce que longueur * 2 est, ils ont des lésions cérébrales. Ce bit est tout au sujet de la lisibilité d'une charge de la merde. Quoi que ce soit par un multiplie nombre entier est DROP DEAD facile à lire: multiplier « chose » par un facteur de « # ». Vous pourriez être en mesure de le rendre « plus évidemment compréhensible » en utilisant une constante: int const scale_factor = 2; int taille = longueur * scale_factor; Cependant, cela a le problème de la demande aux lecteurs d'aller ce chiffre est scale_factor si elles le souhaitent savoir ce qu'ils se multiplient par. Il n'y a aucune garantie l'emplacement de la constante est bien connue.


@Everyone: Il y a une cinquième option qui rend l'intention du code «pas facilement compréhensible» facile à comprendre, constantes ou non: son appelé commentaire.


@jrista +100 pour commentaires pour des cas aussi simples. La réponse est mise à jour.


@incognito: ha! Merci. ;) Je suppose que j'étais un peu dure. Pour moi, cela se résume à ce qui vous donne le bénéfice MOT pour votre fretting. Vous pouvez vous inquiéter sur une doublure oncle trivial comme celle-ci pendant des heures, affirmant la lisibilité et la compréhensibilité lorsqu'un commentaire résoudra le problème ... ou, vous pouvez passer ces heures à résoudre les problèmes beaucoup plus importants ... comme ceux grand Boules de boue Littering votre code (non, attendez, c'est votre code) ... ou la chose qui détruit absolument la performance ... ou ces fonctionnalités manquantes que vos clients menacent de vous abandonner. ..Yeah ...... longueur * 2 ...


@Jrista: Il n'y a aucune garantie que échelle_factor est bien connu, mais il n'est pas non plus garanti que la justification de "multiplier par 2" est bien connue. En règle générale, si la justification de la multiplication par 2 (plutôt que 3 ou 4) est basée sur des informations locales, un nombre magique est probablement surchargé.



1
votes

avec tout compilateur moderne laissé sur 1 ou multiplication par 2 générera le même code. Cela va probablement être une instruction Ajouter une instruction en ajoutant le numéro à lui-même, mais cela dépend de votre cible.

D'autre part, faire (x * 200) / 100 ne sera pas le même que de doubler le nombre car la première multiplication peut déborder, de sorte que cela ne peut donc pas être optimisé en toute sécurité pour doubler le nombre.


0 commentaires

7
votes

qui est plus lisible à votre programmeur moyen:

int size = length * 2;
int size = length << 1;


10 commentaires

Si quelqu'un a besoin d'un commentaire expliquant l'opérateur de travail que quelqu'un ne devrait pas programmer du tout dans mes yeux.


Parce que vous êtes probablement d'un fond C / C ++. À mes yeux, toute utilisation de l'opérateur de travail est une surpopulation. Je pourrais prendre cette première ligne à une personne dans la rue et ils pourraient deviner ce qu'il fait. Essayez cela avec la deuxième ligne. Si le programmeur ne pouvait pas l'exercer en 5 secondes, je serais peut-être concerné, mais c'est juste une pensée supplémentaire inutile pour aucun avantage imaginaire.


@Graphiz: demandez-vous aux personnes de la rue d'écrire votre logiciel ou d'embaucher des programmeurs? Je suis d'accord avec vous, mais le toxicenger a un très bon point.


Il ne le fait vraiment pas. Vous pouvez programmer sans l'opérateur de travail aussi facilement que possible sans l'opérateur ternaire. Et vous tournez mon argument en une paille - je ne suggère pas que notre objectif est de rendre nos programmes lisibles à ceux de la rue, mais s'il y a deux façons de faire quelque chose et que la seule différence est la clarté, il a du sens choisir le plus clair. Le code doit transmettre l'intention.


Si je répondais à notre serveur ou à notre équipe de périphérique mobile, ils pourraient le dire dans une seconde (programmeurs C / C ++). Si je devais le demander à notre équipe de bureau (C #), je suis sûr que la moitié d'entre elles devraient le chercher. Combien de fois avez-vous besoin d'un bitShift pour programmer une interface graphique? ...


@Graphiz: S'il y a des programmeurs qui ne connaissent pas les opérateurs très basiques, vous leur faites une faveur en les faisant chercher. Je suis entièrement d'accord avec vous sur le principe de la simplicité, mais pour l'intention de Dieu, un opérateur de bits changeant? N'est-ce pas CS 101?


À la fin de la journée, le but du logiciel est de résoudre des problèmes, de ne pas être magnifiquement beaux ou super optimisé. Si j'avais d'accord pour dire que la seconde est "plus pure" dans le sens théorique, la première est loin et plus facile à lire - il n'y a aucun sens à héberger à la manière dont elle était comme une justification d'utilisation de la forme plus obscurcée pour Cette opération. Ils font tous les deux exactement la même chose, et je parierai que le compilateur le sait. L'utilisation de la version "exclusive" de l'opération ne sert aucun but.


À mon avis, l'arrogance des programmeurs est l'un des plus grands obstacles du développement de logiciels et du travail d'équipe en général.


@ YA23, je pense que vous prenez trop de difficulté d'une approche, à ce stade, il est tout à fait possible d'être un développeur de logiciels sans jamais avoir à vous soucier de la notation pour une mise en œuvre d'un changement de travail. C'est encore une fois quelque chose que tant que le raisonnement derrière elle est compris, la mise en œuvre dans la pratique signifie absolument rien, peu importe combien vous voulez que votre code soit optimisé pour les compilateurs incorrects.


@ Jean-Bernard Pellerin: correct. Je ne défends pas le code dans ce cas particulier, juste l'argument donné. Le code ne devrait pas être là car il s'agit d'une micro-optimalisation (bien sûr, sauf s'il ya un besoin spécifique et mesuré). Mais dire qu'il est si difficile de lire cela nécessite un commentaire? De nos jours, il est possible d'être un développeur de logiciels qui ne connaissant que la syntaxe très basique d'un seul langage de programmation. Cela signifie-t-il que nous devrions tous buez le code que les programmeurs juniors n'auront pas à apprendre les délégués et les génériques?



6
votes

Qu'est-ce que 200l et 100L représente? Il me semble que vous utilisez numéros magiques . Vous devriez essayer d'écrire votre programme de manière à décrire son intention aussi bonne que possible; rendre la plus lisible que possible. L'utilisation de constantes nommées pour ces valeurs au lieu des numéros magiques est un bon moyen de le faire. L'extraction également du calcul de sa propre méthode bien nommée vous aide également. Lorsque vous faites cela, vous verrez immédiatement qu'il n'y a aucun moyen de la réécrire à x << 1 . Pas parce que les résultats seront différents, mais parce que la maintenabilité en souffrira. Lorsque vous écrivez du code comme x << 1 , le prochain programmeur n'a aucune idée de ce que cela signifie réellement et il augmentera le fameux wtfs par minute :

= "https://i.stack.imgur.com/hg8mr.jpg" rel = "nofollow noreferrer"> alt Texte
(source: OSNews.com )


Je pense que votre code devrait ressembler davantage à ceci: xxx

Bien sûr, les noms de ces valeurs const sont une supposition sauvage: -)


1 commentaires

Le 2001 / 100L n'est pas mon propre code ... Je viens de tomber sur elle et j'ai demandé à propos de Sematic Sematic Différences (que je pense qu'il pourrait y avoir, puisque (1) jette à long). Ce n'est pas un concours de beauté. Donc, je suppose que votre réponse est juste un peu hors de la marque.



43
votes

L'idée que est plus rapide que la multiplication est le raisonnement comme si le compilateur .NET JIT est en fait un compilateur C mal optimisé écrit dans les années 1970. Même s'il était vrai, la différence serait mesurée en picosecondes à ce stade, même s'il y avait une différence, ce qui n'est probablement pas.

écrivez le code de sorte qu'il soit facile de lire em>. Laissez le compilateur prendre soin des optimisations Pico. Optimisez votre code basé sur le profilage des scénarios réalistes, pas sur la seconde deviner ce que le compilateur générera. P>

En outre, les opérateurs de décalage font pas em> ont la même sémantique que la multiplication. Par exemple, envisagez la séquence suivante des modifications: P>

Programme original par JILL: P>

int x = y << 1 + 1;


10 commentaires

Je suis content que quelqu'un l'a pointue. J'aime le mot "optimisations pico". Dans notre langue, nous appelons ce "BitNeuken" (je ne peux pas le traduire, ou je serai banni :-)


Je suis surpris que tant de gens pensent que x << 1 est moins lisible que x * 2 . Si je veux me multiplier par une puissance de 2, j'utilise habituellement le quart de travail, car il contraint de contraindre le multiplicand, stipule donc que l'intention est encore mieux. Et comment écrivez-vous des valeurs pour votre [indicateurs] Enums ??? Vous utilisez sûrement hex ou shift, pas le dénonage ???


@ToxicAvenger: Vous ne pouvez pas comparer les drapeaux bitmapped avec une multiplication simple. Si vous voulez multiplier par 32, il est beaucoup plus lisible pour dire la valeur * 32 au lieu de la valeur << 5. Les opérateurs BitShift ont leur utilisation, mais pas pour se multiplier. Et si vous deviez modifier votre code plus tard en valeur * 31?


Droit. La réclamation n'est pas que BitShifting est justifiée car il est plus lisible, mais plutôt que c'est justifié car il est plus rapide. Je dis que cela n'a pas d'importance, ce qui est plus rapide: lorsque l'opération que vous voulez est logiquement multiplier un nombre alors multipliez . Lorsque l'opération que vous voulez est logiquement manipuler des bits alors manipule des bits . Lorsque vous traitez un numéro sous forme de tableau BIT, vous opérez au mauvais niveau d'abstraction. Le fait que des chiffres soient mis en œuvre sous forme de tableaux de bits ne doivent pas être exploités à moins d'une raison une raison de le faire.


Supprimé. Ouvrirait une autre canette de vers. Bonne nuit.


+10 pour "Lorsque vous traitez un numéro sous forme de tableau BIT, vous opérez au mauvais niveau d'abstraction.". Eric est toujours à taper.


@ERIFLIPPERT HI, sauf si elle est dans un tas de boucles imbriquées et il est exécuté quelques-unes 2e + 13 fois, ce qui signifie qu'une économie de picoseconde donnera 20 ans d'économie, êtes-vous d'accord?


@jambodev: Je suis d'accord avec les maths. Mais ce n'est pas une justification d'une optimisation prématurée. Écrivez le code comme une personne sensible, utilisez un profileur pour trouver les points chauds, puis les optimiser correctement. Cela pourrait inclure, par exemple, paralléiser les boucles à un GPU, qui semble susceptible de sauvegarder beaucoup plus de 20 secondes. Vous voulez rechercher le la plus grande victoire possible contre votre pire problème plutôt que d'espérer que la nanoseconde gagne ajoutez.


@ERICLIPPERT Je conviens que dans un système de boucles aussi imbriqués, cette opération semble peu probable d'être notre goulot d'étranglement principal.


Contexte compte. Comme indiqué, nous n'avons pas de contexte pour la question. Sauf pour le soupçon d'une étiquette d'algorithme. Et dans un algorithme, vous voudriez optimiser ses performances. Le paradoxe sur l'argument selon lequel les personnes utilisant l'optimisation font des hypothèses sur le compilateur C # sont qu'ils sont exactement et manifestement correct; C'est l'hypothèse opposée que le compilateur prendrait soin de ce qui est faux.



7
votes

Il est intéressant de noter que la plupart des réponses affirment que le compilateur optimisera une multiplication par une puissance de 2 dans un bitShift. Il est évident qu'aucun des intervenants n'a réellement essayé de compiler un bitShift par rapport à un multiplier pour voir ce que le compilateur produit réellement.

C'est purement un exercice académique; Comme à peu près tout le monde l'a souligné, le multiplicateur est plus facile à lire (bien que la partie "* 200l / 100l" est là où il y a une suppose de quiconque - qui sert simplement à obscurcir les choses). Il est également assez évident que le remplacement d'un multiplicateur avec un bit-bit dans C # n'aura aucune augmentation significative de la performance, même dans des boucles serrées. Si vous avez besoin de ce type d'optimisation, vous utilisez la mauvaise plate-forme et la mauvaise langue pour commencer. P>

Voyons ce qui se passe lorsque nous compilons un programme simple avec le CSC (le compilateur C #) inclus avec Visual Studio 2010 avec optimisations activées. Voici le premier programme: P>

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       23 (0x17)
  .maxstack  2
  .locals init ([0] int32 j,
           [1] int32 i)
  IL_0000:  ldc.i4.1
  IL_0001:  stloc.0
  IL_0002:  ldc.i4.0
  IL_0003:  stloc.1
  IL_0004:  br.s       IL_000e
  IL_0006:  ldloc.0
  IL_0007:  ldc.i4.2
  IL_0008:  shl
  IL_0009:  stloc.0
  IL_000a:  ldloc.1
  IL_000b:  ldc.i4.1
  IL_000c:  add
  IL_000d:  stloc.1
  IL_000e:  ldloc.1
  IL_000f:  ldc.i4     0x186a0
  IL_0014:  blt.s      IL_0006
  IL_0016:  ret
} // end of method Program::Main


3 commentaires

Eh bien c'est l'IL. Je pense que les plus grandes parties des optimisations se produisent dans le JIT, vous devrez donc consulter le code de montage généré par le JIT et vous assurer que toutes les optimisations sont activées (le débogage de VS par exemple génère des attributs qui manipulent le comportement et vous pouvez également. manipuler le comportement JIT par le fichier INI). Basé sur mon expérience, en regardant l'IL ne peut que peu à aucun lorsque vous parlez de «Pico» -Optimizations.


Le fait que le compilateur C # n'offre pas, ne signifie pas que le compilateur JIT ne le fera pas. Un compilateur JIT décent optimisera le code qu'il génère. Après tout, ce que le compilateur C # génère n'est que du code intermédiaire.


Si vous compilez x * 2; et x << 1; En C, vous obtenez une assemblée équivalente (à gauche à gauche pour les deux), ce qui est probablement pourquoi la plupart des gens disent que le compilateur optimisera la multiplication. Clairement, le compilateur C # ne (au moins pour votre exemple), mais le JIT pourrait.



1
votes

Pour répondre à votre question actuelle, il semble n'y avoir aucune différence de comportement entre ces deux snippings de code, lorsque longueur est un in int non négatif et le texte du code est dans un non contrôlé contexte.

dans un coché contexte (int.maxvalue * 200l) ne déborde jamais de déborder car le résultat s'inscrit facilement dans un long . (int) ((longueur * 200L) / 100L ne débordera que lorsque longueur * 2 débords. En aucun cas un opérateur de décalage débordement. Par conséquent, les snippings de code se comportent différemment. dans un coché contexte.

si longueur est négatif, l'opération de décalage produira des résultats incorrects, tandis que la multiplication fonctionnera correctement. Par conséquent, les deux snippits de code sont différents si la variable est autorisée à être négative.

(Contrairement à l'opinion populaire X << 1 n'est pas la même chose que x * 2. Ils ne sont que les mêmes si X est un entier non signé.)


0 commentaires

3
votes

Quand je vois l'idiome: xxx

Je pense que l'intention du code est de faire la mise à l'échelle de quelque manière que ce soit. Il pourrait s'agir d'un code de point fixe manuel ou d'éviter des problèmes avec une division entière. Un exemple concret: xxx

ou écrit pour éviter d'aller et d'aller au point flottant: xxx

quand je vois ceci idiome: xxx

Je me demande brièvement si les bits individuels de quelque part ont un sens plus profond que cela nécessite le changement de vitesse et je commence ensuite à regarder le contexte environnant pour comprendre pourquoi il a été fait de cette façon .

La chose à retenir est que lorsque vous écrivez un code qui a quelque chose comme ceci: xxx

qu'il y a un nombre infini de façons de créer Expr, telle que Val aura toujours la même valeur. Il est important de considérer quelle intention vous exprimez dans ExPR pour ceux qui suivront.


0 commentaires

0
votes

Peu importe la lisibilité de code: la multiplication de bit-shift et de la multiplication entière, même par des pouvoirs constants de deux, ne sont souvent pas identiques.

aucun compilateur ne serait "optimiser" x * 2 code> à X Sauf s'il ne peut prouver que X est un entier non négatif. (Si le type de x est non signé INT code>, cela est bien sûr vrai par définition.) Il aurait également besoin de savoir que le comportement de trop-plein est identique ou que le débordement ne peut pas se produire. Considérez l'exemple suivant, en C #: P>

int x = 2000000000, multiplied, shifted;
// Intention is to store results in 64-bit in case x is large:
long multA, shiftA, multB, shiftB;
checked
{
shifted = x << 1; // Overflows to negative number.
multiplied = x * 2; // Throws OverflowException.
shiftA = x << 1; // Maybe this overflows to negative number too?
multA = x * 2; // Maybe this throws OverflowException too?
shiftB = x << 1L; // Happens to double OK, because x is positive.
multB = x * 2L; // Works regardless of the value of x.
}


0 commentaires