2010-09-07 4 views

Répondre

12

Non défini. Vous demandez explicitement C# - mais cela dépend de l'architecture du processeur, c'est-à-dire du compilateur d'exécution CLR.

+1

Je suppose que cela est vrai sur la plupart des langages de programmation. – Andy

+0

... et ce qui est meilleur est déterminé par ce qui est plus lisible dans le contexte où il est utilisé. –

44

Ni l'un ni l'autre; ils devraient tous les deux se compiler à la même chose si l'on est plus rapide ou meilleur. Plus important encore, la plupart des programmeurs trouveront probablement > 0 plus lisibles, et la lisibilité est plus importante que les sous-optimisations micro comme ceci.

+7

+1 pour souligner l'importance de la lisibilité – Matt

+7

Je ne sais pas à propos de votre assertion de lisibilité; J'ai lu> = 1 comme "1 ou plus", et c'est une façon plus naturelle de définir une condition que "plus de zéro". Cependant, "plus de cinq" prend tout son sens dans la conversation. Je pense que la lisibilité dépendrait du contexte, et de la présence et de la formulation des documents d'exigences ou des guides de l'utilisateur que les développeurs examineraient parallèlement au code. Cela dépend aussi beaucoup du type de maths impliqués; Comme l'a dit Jesse Millikan, les deux comparaisons ne seraient pas équivalentes dans le monde flottant. – KeithS

+0

@KiethS: J'ai lu l'intention de '> 0' comme" nombre entier positif non nul ". En lisant '> = 1', je dois réfléchir à la façon dont les nombres entiers n'ont pas de chiffres décimaux pour décider de cela. Et oui, les expressions ne sont pas les mêmes; bien que je n'ai pas fait de vastes programmes à virgule flottante avant. Y a-t-il des cas où '> = 1' sera meilleur? Sûr. Mais étant donné la façon dont la question est écrite, j'ai supposé que "l'entier positif non nul" était l'intention. –

7

La différence de performance va être négligeable entre les deux (s'il y en a même une). Je travaille à prouver exactement ce que cela pourrait être (cela dépendra de la plate-forme puisque tout différent dépendra probablement du code émis et exécuté par JIT). Gardez à l'esprit, cependant, que c'est une micro-optimisation extrême qui est probablement injustifiée. Le meilleur choix sera celui qui sera toujours plus lisible et transmettra votre intention le meilleur dans votre code.

1

Pour répondre à la plus rapide, je ne suis pas sûr, mais je pense qu'ils sont équivalents. Et pour répondre au mieux, je pense que cela dépend du contexte.

0

Vous ne remarquerez aucune différence, sauf dans une boucle très très serrée qui est essentielle à la performance de votre application. Ensuite, vous avez besoin de profiler votre code de toute façon pour décider lequel est le meilleur.

Utilisez celui qui a le plus de sens dans votre application.

26

Celui qui est le meilleur est celui qui exprime le plus clairement votre intention.

Si vous testez pour voir si un entier est dans l'intervalle [1, 6], alors vous devriez l'écrire comme:

if (x >= 1 && x <= 6) { ... } 

écriture cela fonctionnerait, mais ne remplit pas évidemment la spécification :

if (x > 0 && x < 7) { ... } 

Je suppose également que vous parlez de types entiers ici. Si vous traitez des nombres décimaux ou décimaux, ils ne sont pas équivalents.


Sauf si vous avez PROFILES votre code et trouvé que ce soit le goulot d'étranglement, vous ne devriez pas vous soucier de micro-OPTIMISATIONS. Même ainsi, il peut être intéressant d'inspecter le code généré par le compilateur C# dans chaque cas pour voir si elles ont compilé à la même IL ou non. Cela peut être fait en utilisant .NET Reflector.

if (x >= 1) 
{ 
    Console.WriteLine("True!"); 
} 

Résultats dans:

L_000b: ldloc.0   // Load the value of x 
L_000c: ldc.i4.1   // Load the constant 1 
L_000d: blt.s L_0019  // Branch if less than 
L_000f: ldstr "True!" 
L_0014: call void [mscorlib]System.Console::WriteLine(string) 

Attendu que:

if (x > 0) 
{ 
    Console.WriteLine("True!"); 
} 

résultats dans l'IL suivantes:

L_000b: ldloc.0   // Load the value of x 
L_000c: ldc.i4.0   // Load the constant 0 
L_000d: ble.s L_0019  // Branch if less than or equal 
L_000f: ldstr "True!" 
L_0014: call void [mscorlib]System.Console::WriteLine(string) 

Dans les deux cas, le compilateur a inversé la comparaison. Le test "supérieur ou égal à" a été compilé à une instruction "inférieure à" et le test "supérieur à" a été compilé à "inférieur ou égal à". En général, le compilateur est libre de faire de telles modifications et l'exécution d'une version différente du compilateur peut produire un bytecode différent (mais équivalent). Etant donné qu'ils ne compilent pas à la même IL, la meilleure façon de voir ce qui est le plus rapide est d'exécuter le code dans une boucle et de voir combien de temps chaque version doit exécuter. J'ai essayé de le faire mais je n'ai vu aucune différence de performance mesurable entre les deux façons d'écrire le code.

+0

Je suis d'accord! C'est ce qu'on attend de la déclaration ...> 0 et> = 1 fait vraiment 2 choses différentes (et parfois même) ... soit vous voulez tout ou partie de celui-ci, que rien de tout cela! –

+0

J'aime que vous montriez l'IL, surtout quand il bascule la comparaison dans le bytecode. – Nick

0

Donc généralement lorsque je compare quelque chose à> 0 ou> = 1, j'essaie de voir si un tableau/collection contient des éléments. Si c'est le cas, au lieu d'utiliser .Count > 0, essayez d'utiliser la méthode d'assistance Enumerable.Any() dans System.Linq qui devrait être beaucoup plus rapide.

Sinon, je ne sais pas :)

+0

Pourquoi 'Any()' serait-il plus rapide? Je pense que ce serait plus lent, puisque 'Count' est juste un accesseur de propriété mais' Any() 'doit énumérer le premier terme de la séquence. – jnylen

+0

@jnylen: Dépend du nombre que vous utilisez. 'Enumerable.Count()' est bien pire que 'Enumerable.Any()'. Si votre conteneur a une propriété 'Count' (c'est-à-dire' List') alors oui, vous devriez l'utiliser. –

+2

Je pense que .Any() évalue à .Count> 0 si la collection supporte ce membre. Et c'est toujours plus rapide que .Count() quand la collection ne le supporte pas. Je ne sais pas ce qu'il compile IL, donc ça peut ne pas être beaucoup d'aide ... –

2

Il n'y a pas de différence parce que le cpu ne interne une soustraction des deux nombres et inspecte le résultat et le débordement. Il n'y a pas d'étape supplémentaire impliquée pour l'une ou l'autre instruction.

En ce qui concerne le code, cela dépend de ce que vous essayez de documenter. > = 1 signifie que 1 est le nombre le plus bas possible. > 0 signifie que 0 n'est pas autorisé. Il y a une petite différence sémantique que les professionnels remarqueront. Ils choisiront le bon opérateur pour documenter leur intention.

Si vous pensez que> = n et> = n + 1 sont les mêmes que vous vous trompez:> = Int.MaxValue et> (Int.MaxValue + 1) sont différents ^^

3

Je suis d'accord avec le d'autres réponses que les micro-optimisations ne devraient généralement pas être prises en compte. Cependant, il peut être intéressant de voir laquelle des deux versions a le plus petit/appear_faster IL.

Alors:

using System; 

namespace IL_Test 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      int i = 3; 
      if (i > 0) 
      { 
       Console.Write("i is greater than zero"); 
      } 
     } 
    } 
} 

se traduit par:

(debug)

.method private hidebysig static void Main(string[] args) cil managed 
{ 
    .entrypoint 
    .maxstack 2 
    .locals init (
     [0] int32 i, 
     [1] bool CS$4$0000) 
    L_0000: nop 
    L_0001: ldc.i4.3 
    L_0002: stloc.0 
    L_0003: ldloc.0 
    L_0004: ldc.i4.0 
    L_0005: cgt 
    L_0007: ldc.i4.0 
    L_0008: ceq 
    L_000a: stloc.1 
    L_000b: ldloc.1 
    L_000c: brtrue.s L_001b 
    L_000e: nop 
    L_000f: ldstr "i is greater than zero" 
    L_0014: call void [mscorlib]System.Console::Write(string) 
    L_0019: nop 
    L_001a: nop 
    L_001b: ret 
} 

(PRESSE)

.method private hidebysig static void Main(string[] args) cil managed 
{ 
    .entrypoint 
    .maxstack 2 
    .locals init (
     [0] int32 i) 
    L_0000: ldc.i4.3 
    L_0001: stloc.0 
    L_0002: ldloc.0 
    L_0003: ldc.i4.0 
    L_0004: ble.s L_0010 
    L_0006: ldstr "i is greater than zero" 
    L_000b: call void [mscorlib]System.Console::Write(string) 
    L_0010: ret 
} 

tout

using System; 

namespace IL_Test 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      int i = 3; 
      if (i >= 1) 
      { 
       Console.Write("i is greater than zero"); 
      } 
     } 
    } 
} 

dans

(debug)

.method private hidebysig static void Main(string[] args) cil managed 
{ 
    .entrypoint 
    .maxstack 2 
    .locals init (
     [0] int32 i, 
     [1] bool CS$4$0000) 
    L_0000: nop 
    L_0001: ldc.i4.3 
    L_0002: stloc.0 
    L_0003: ldloc.0 
    L_0004: ldc.i4.1 
    L_0005: clt 
    L_0007: stloc.1 
    L_0008: ldloc.1 
    L_0009: brtrue.s L_0018 
    L_000b: nop 
    L_000c: ldstr "i is greater than zero" 
    L_0011: call void [mscorlib]System.Console::Write(string) 
    L_0016: nop 
    L_0017: nop 
    L_0018: ret 
} 

(PRESSE)

.method private hidebysig static void Main(string[] args) cil managed 
{ 
    .entrypoint 
    .maxstack 2 
    .locals init (
     [0] int32 i) 
    L_0000: ldc.i4.3 
    L_0001: stloc.0 
    L_0002: ldloc.0 
    L_0003: ldc.i4.1 
    L_0004: blt.s L_0010 
    L_0006: ldstr "i is greater than zero" 
    L_000b: call void [mscorlib]System.Console::Write(string) 
    L_0010: ret 
} 

Pour autant que je peux voir le i> = 1 est légèrement plus rapide que i> 0 IN MODE DEBUG

En mode de déclenchement l la différence est à un décalage de 0004 a BLE par rapport à un BLT. Je suppose que ces deux opérations IL se traduisent par des ops natifs consommant également du CPU.

+0

Est-ce que ces échangés ou quelque chose me manque? –

+2

Vous dites que l'un d'entre eux est légèrement plus rapide - pourquoi n'avez-vous pas publié les résultats de votre test de performance? Sur quelle machine avez-vous testé? Optimisations activées? L'optimisation est faite par le JIT, pas par le compilateur C#, donc regarder l'IL généré ne dit pas grand-chose sur les performances. – Niki

+2

@Caspar: le premier fait: 'if (i> 0) == false', puis retourne (ne console.write pas). La seconde est en train de faire: 'if (i <1) == true', alors revenez. L'IL produit par le test d'Andrei semble être beaucoup plus verbeux que ce que Mark Byers a trouvé. Point fait par d'autres: l'exécuter, l'évaluer, demander est-il vraiment plus lent? Le code ici peut être DEBUG compilé, ou avec une autre version du compilateur .. nous ne savons pas. Oui, JIT peut optimiser plus ou pas. Benchmark it .. – maxwellb

4

Bien sûr, cela dépend de l'architecture de l'UC sur laquelle votre programme sera exécuté. Sur x86, les instructions jge et jg, qui sont pertinentes ici, prennent le même nombre de cycles IIRC. Dans le cas spécifique de test pour> 0, si vous utilisez des entiers non signés, il peut (je ne sais vraiment pas) être plus rapide à utiliser l'instruction test au lieu de cmp, puisque pour les entiers non signés> 0 est équivalent à! = 0 D'autres architectures peuvent être différentes. Le fait est que c'est si bas niveau que, même dans les rares cas où cela vaut la peine d'être optimisé, il n'y a pas de moyen indépendant du matériel pour l'optimiser.

Edit: Oublié de mentionner: Tout compilateur ou VM digne de ce nom devrait être capable de comprendre que tester> = 1 est équivalent à tester> 0 et effectuer une telle optimisation triviale s'il fait même une différence dans la langue d'assemblage niveau.

0

S'il y avait une différence entre les deux, alors je dirais que ce serait une telle micro-optimisation, ce qui ne devrait pas affecter les performances globales de l'application. De plus, quand on se demande vraiment s'il doit utiliser> 0 ou> = 1, je dirais que le coût pour déterminer lequel est le plus rapide ne l'emporte pas sur le gain de performance (minimal). Par conséquent, je dirais aussi que vous devriez utiliser l'option qui exprime le plus l'intention.

Questions connexes