L’opérateur ternaire est deux fois plus lent qu’un bloc if-else?

Je lis partout où l’opérateur ternaire est supposé être plus rapide ou au moins identique à son équivalent if – bloc d’ else .

Cependant, j’ai fait le test suivant et j’ai découvert que ce n’était pas le cas:

 Random r = new Random(); int[] array = new int[20000000]; for(int i = 0; i  0) { value += 2; } else { value += 3; } // if-else block above takes on average 85 ms // OR I can use a ternary operator: // value += i > 0 ? 2 : 3; // takes 157 ms } DateTime end = DateTime.UtcNow; MessageBox.Show("Measured time: " + (end-begin).TotalMilliseconds + " ms.\r\nResult = " + value.ToSsortingng()); 

Mon ordinateur a mis 85 ms pour exécuter le code ci-dessus. Mais si je commente le bloc ifelse et décommente la ligne d’opérateur ternaire, cela prendra environ 157 ms.

Pourquoi cela arrive-t-il?

Pour répondre à cette question, nous examinerons le code assembleur produit par les JIT X86 et X64 pour chacun de ces cas.

X86, si / alors

  32: foreach (int i in array) 0000007c 33 D2 xor edx,edx 0000007e 83 7E 04 00 cmp dword ptr [esi+4],0 00000082 7E 1C jle 000000A0 00000084 8B 44 96 08 mov eax,dword ptr [esi+edx*4+8] 33: { 34: if (i > 0) 00000088 85 C0 test eax,eax 0000008a 7E 08 jle 00000094 35: { 36: value += 2; 0000008c 83 C3 02 add ebx,2 0000008f 83 D7 00 adc edi,0 00000092 EB 06 jmp 0000009A 37: } 38: else 39: { 40: value += 3; 00000094 83 C3 03 add ebx,3 00000097 83 D7 00 adc edi,0 0000009a 42 inc edx 32: foreach (int i in array) 0000009b 39 56 04 cmp dword ptr [esi+4],edx 0000009e 7F E4 jg 00000084 30: for (int x = 0; x < iterations; x++) 000000a0 41 inc ecx 000000a1 3B 4D F0 cmp ecx,dword ptr [ebp-10h] 000000a4 7C D6 jl 0000007C 

X86, ternaire

  59: foreach (int i in array) 00000075 33 F6 xor esi,esi 00000077 83 7F 04 00 cmp dword ptr [edi+4],0 0000007b 7E 2D jle 000000AA 0000007d 8B 44 B7 08 mov eax,dword ptr [edi+esi*4+8] 60: { 61: value += i > 0 ? 2 : 3; 00000081 85 C0 test eax,eax 00000083 7F 07 jg 0000008C 00000085 BA 03 00 00 00 mov edx,3 0000008a EB 05 jmp 00000091 0000008c BA 02 00 00 00 mov edx,2 00000091 8B C3 mov eax,ebx 00000093 8B 4D EC mov ecx,dword ptr [ebp-14h] 00000096 8B DA mov ebx,edx 00000098 C1 FB 1F sar ebx,1Fh 0000009b 03 C2 add eax,edx 0000009d 13 CB adc ecx,ebx 0000009f 89 4D EC mov dword ptr [ebp-14h],ecx 000000a2 8B D8 mov ebx,eax 000000a4 46 inc esi 59: foreach (int i in array) 000000a5 39 77 04 cmp dword ptr [edi+4],esi 000000a8 7F D3 jg 0000007D 57: for (int x = 0; x < iterations; x++) 000000aa FF 45 E4 inc dword ptr [ebp-1Ch] 000000ad 8B 45 E4 mov eax,dword ptr [ebp-1Ch] 000000b0 3B 45 F0 cmp eax,dword ptr [ebp-10h] 000000b3 7C C0 jl 00000075 

X64, si / alors

  32: foreach (int i in array) 00000059 4C 8B 4F 08 mov r9,qword ptr [rdi+8] 0000005d 0F 1F 00 nop dword ptr [rax] 00000060 45 85 C9 test r9d,r9d 00000063 7E 2B jle 0000000000000090 00000065 33 D2 xor edx,edx 00000067 45 33 C0 xor r8d,r8d 0000006a 4C 8B 57 08 mov r10,qword ptr [rdi+8] 0000006e 66 90 xchg ax,ax 00000070 42 8B 44 07 10 mov eax,dword ptr [rdi+r8+10h] 33: { 34: if (i > 0) 00000075 85 C0 test eax,eax 00000077 7E 07 jle 0000000000000080 35: { 36: value += 2; 00000079 48 83 C5 02 add rbp,2 0000007d EB 05 jmp 0000000000000084 0000007f 90 nop 37: } 38: else 39: { 40: value += 3; 00000080 48 83 C5 03 add rbp,3 00000084 FF C2 inc edx 00000086 49 83 C0 04 add r8,4 32: foreach (int i in array) 0000008a 41 3B D2 cmp edx,r10d 0000008d 7C E1 jl 0000000000000070 0000008f 90 nop 30: for (int x = 0; x < iterations; x++) 00000090 FF C1 inc ecx 00000092 41 3B CC cmp ecx,r12d 00000095 7C C9 jl 0000000000000060 

X64, ternaire

  59: foreach (int i in array) 00000044 4C 8B 4F 08 mov r9,qword ptr [rdi+8] 00000048 45 85 C9 test r9d,r9d 0000004b 7E 2F jle 000000000000007C 0000004d 45 33 C0 xor r8d,r8d 00000050 33 D2 xor edx,edx 00000052 4C 8B 57 08 mov r10,qword ptr [rdi+8] 00000056 8B 44 17 10 mov eax,dword ptr [rdi+rdx+10h] 60: { 61: value += i > 0 ? 2 : 3; 0000005a 85 C0 test eax,eax 0000005c 7F 07 jg 0000000000000065 0000005e B8 03 00 00 00 mov eax,3 00000063 EB 05 jmp 000000000000006A 00000065 B8 02 00 00 00 mov eax,2 0000006a 48 63 C0 movsxd rax,eax 0000006d 4C 03 E0 add r12,rax 00000070 41 FF C0 inc r8d 00000073 48 83 C2 04 add rdx,4 59: foreach (int i in array) 00000077 45 3B C2 cmp r8d,r10d 0000007a 7C DA jl 0000000000000056 57: for (int x = 0; x < iterations; x++) 0000007c FF C1 inc ecx 0000007e 3B CD cmp ecx,ebp 00000080 7C C6 jl 0000000000000048 

Premièrement: pourquoi le code X86 est- il plus lent que X64?

Cela est dû aux caractéristiques suivantes du code:

  1. X64 dispose de plusieurs registres supplémentaires disponibles, et chaque registre est de 64 bits. Cela permet au JIT X64 d'exécuter la boucle interne entièrement à l'aide de registres, à l'exception du chargement de la masortingce, tandis que le JIT X86 place plusieurs opérations de stack (access mémoire) dans la boucle.
  2. value est un entier de 64 bits, qui nécessite 2 instructions machine sur X86 ( add suivi de adc ) mais seulement 1 sur X64 ( add ).

Deuxièmement: pourquoi l’opérateur ternaire est-il plus lent sur X86 et X64?

Cela est dû à une différence subtile dans l'ordre des opérations affectant l'optimiseur de JIT. Pour JIT l'opérateur ternaire, plutôt que de coder directement les instructions 2 et 3 dans les instructions d' add machine, le JIT crée une variable intermédiaire (dans un registre) pour contenir le résultat. Ce registre est ensuite étendu de 32 bits à 64 bits avant de l’append à la value . Étant donné que tout cela est effectué dans des registres pour X64, malgré l'augmentation significative de la complexité pour l'opérateur ternaire, l'impact net est quelque peu réduit.

Le JIT X86, quant à lui, subit un impact plus important, car l'ajout d'une nouvelle valeur intermédiaire dans la boucle interne provoque une "perte" d'une autre valeur, entraînant au moins 2 access mémoire supplémentaires dans la boucle interne (voir les access). à [ebp-14h] dans le code ternaire X86).

EDIT: Tous les changements … voir ci-dessous.

Je ne peux pas reproduire vos résultats sur le CLR x64, mais je peux le faire sur x86. Sur x64, je peux voir une petite différence (moins de 10%) entre l’opérateur conditionnel et l’opérateur if / else, mais il est beaucoup plus petit que ce que vous voyez.

J’ai apporté les modifications potentielles suivantes:

  • Exécuter dans une application console
  • Construire avec /o+ /debug- et exécuter en dehors du débogueur
  • Exécutez les deux morceaux de code une fois pour les JIT, puis beaucoup de fois pour plus de précision
  • Utiliser le Stopwatch

Résultats avec /platform:x64 (sans les lignes “ignore”):

 if/else with 1 iterations: 17ms conditional with 1 iterations: 19ms if/else with 1000 iterations: 17875ms conditional with 1000 iterations: 19089ms 

Résultats avec /platform:x86 (sans les lignes “ignore”):

 if/else with 1 iterations: 18ms conditional with 1 iterations: 49ms if/else with 1000 iterations: 17901ms conditional with 1000 iterations: 47710ms 

Les détails de mon système:

  • x64 Processeur i7-2720QM @ 2,20 GHz
  • Windows 8 64 bits
  • .NET 4.5

Donc, contrairement à avant, je pense que vous constatez une réelle différence – et tout est lié au JIT x86. Je ne voudrais pas dire exactement ce qui cause la différence – je pourrais mettre à jour le post plus tard avec plus de détails si je peux me donner la peine d’aller dans cordbg 🙂

Il est intéressant de noter que sans sorting préalable du tableau, je me retrouve avec des tests qui prennent environ 4,5 fois plus de temps, au moins sur x64. Je suppose que cela concerne la prédiction de twig.

Code:

 using System; using System.Diagnostics; class Test { static void Main() { Random r = new Random(0); int[] array = new int[20000000]; for(int i = 0; i < array.Length; i++) { array[i] = r.Next(int.MinValue, int.MaxValue); } Array.Sort(array); // JIT everything... RunIfElse(array, 1); RunConditional(array, 1); // Now really time it RunIfElse(array, 1000); RunConditional(array, 1000); } static void RunIfElse(int[] array, int iterations) { long value = 0; Stopwatch sw = Stopwatch.StartNew(); for (int x = 0; x < iterations; x++) { foreach (int i in array) { if (i > 0) { value += 2; } else { value += 3; } } } sw.Stop(); Console.WriteLine("if/else with {0} iterations: {1}ms", iterations, sw.ElapsedMilliseconds); // Just to avoid optimizing everything away Console.WriteLine("Value (ignore): {0}", value); } static void RunConditional(int[] array, int iterations) { long value = 0; Stopwatch sw = Stopwatch.StartNew(); for (int x = 0; x < iterations; x++) { foreach (int i in array) { value += i > 0 ? 2 : 3; } } sw.Stop(); Console.WriteLine("conditional with {0} iterations: {1}ms", iterations, sw.ElapsedMilliseconds); // Just to avoid optimizing everything away Console.WriteLine("Value (ignore): {0}", value); } } 

La différence n’a pas grand chose à voir avec if / else vs ternary.

En regardant les désassemblages truqués (je ne vais pas les repasser ici, veuillez consulter la réponse de @ 280Z28), il s’avère que vous comparez des pommes et des oranges . Dans un cas, vous créez deux opérations += différentes avec des valeurs constantes et celle que vous choisissez dépend d’une condition, et dans l’autre cas, vous créez un += où la valeur à append dépend d’une condition.

Si vous voulez vraiment comparer si / sinon vs ternaire, ce serait une comparaison plus juste (maintenant les deux seront tout aussi “lents”, ou nous pourrions même dire que ternaire est un peu plus rapide):

 int diff; if (i > 0) diff = 2; else diff = 3; value += diff; 

contre.

 value += i > 0 ? 2 : 3; 

Maintenant, le déassembly pour le if/else devient comme indiqué ci-dessous. Notez que c’est un peu pire que le cas ternaire, car il a également quitté les registres de la variable de boucle ( i ).

  if (i > 0) 0000009d cmp dword ptr [ebp-20h],0 000000a1 jle 000000AD { diff = 2; 000000a3 mov dword ptr [ebp-24h],2 000000aa nop 000000ab jmp 000000B4 } else { diff = 3; 000000ad mov dword ptr [ebp-24h],3 } value += diff; 000000b4 mov eax,dword ptr [ebp-18h] 000000b7 mov edx,dword ptr [ebp-14h] 000000ba mov ecx,dword ptr [ebp-24h] 000000bd mov ebx,ecx 000000bf sar ebx,1Fh 000000c2 add eax,ecx 000000c4 adc edx,ebx 000000c6 mov dword ptr [ebp-18h],eax 000000c9 mov dword ptr [ebp-14h],edx 000000cc inc dword ptr [ebp-28h] 

Modifier:

Ajout d’un exemple qui peut être fait avec l’instruction if-else mais pas l’opérateur conditionnel.


Avant la réponse, s’il vous plaît jetez un coup d’œil à [ Quel est le plus rapide? ] sur le blog de M. Lippert. Et je pense que la réponse de M. Ersönmez est la plus correcte ici.

J’essaie de mentionner quelque chose que nous devrions garder à l’esprit avec un langage de programmation de haut niveau.

Tout d’abord, je n’ai jamais entendu dire que l’opérateur conditionnel était censé être plus rapide ou la performance équivalente avec l’instruction if-else en C♯ .

La raison est simple: que se passe-t-il s’il n’y a pas d’opération avec l’instruction if-else:

 if (i > 0) { value += 2; } else { } 

L’exigence de l’opérateur conditionnel est qu’il doit y avoir une valeur de chaque côté, et dans C♯, il faut aussi que les deux côtés de : aient le même type. Cela le rend différent de l’instruction if-else. Ainsi, votre question devient une question demandant comment l’instruction du code machine est générée pour que la différence de performance.

Avec l’opérateur conditionnel, c’est sémantiquement:

Quelle que soit l’expression évaluée, il y a une valeur.

Mais avec la déclaration if-else:

Si l’expression est évaluée à true, faites quelque chose; sinon, faire autre chose.

Une valeur n’est pas nécessairement impliquée dans l’instruction if-else. Votre hypothèse n’est possible qu’avec l’optimisation.

Un autre exemple pour démontrer la différence entre eux serait comme suit:

 var array1=new[] { 1, 2, 3 }; var array2=new[] { 5, 6, 7 }; if(i>0) array1[0]=4; else array2[0]=4; 

le code ci-dessus comstack, cependant, remplacez l’instruction if-else par l’opérateur conditionnel ne comstackra tout simplement pas:

 var array1=new[] { 1, 2, 3 }; var array2=new[] { 5, 6, 7 }; (i>0?array1[0]:array2[0])=4; // incorrect usage 

L’opérateur conditionnel et les instructions if-else sont conceptuels identiques lorsque vous faites la même chose, peut-être même plus rapidement avec l’opérateur conditionnel en C , car C est plus proche de l’assemblage de la plate-forme.


Pour le code d’origine que vous avez fourni, l’opérateur conditionnel est utilisé dans une boucle foreach, ce qui gâcherait la différence entre eux. Je propose donc le code suivant:

 public static class TestClass { public static void TestConditionalOperator(int i) { long value=0; value+=i>0?2:3; } public static void TestIfElse(int i) { long value=0; if(i>0) { value+=2; } else { value+=3; } } public static void TestMethod() { TestConditionalOperator(0); TestIfElse(0); } } 

et ce qui suit sont deux versions de l’IL optimisées et non. Comme ils sont longs, j’utilise une image pour montrer, le côté droit est celui optimisé:

(Cliquez pour voir l’image en taille réelle.) hSN6s.png

Dans les deux versions de code, l’IL de l’opérateur conditionnel semble plus court que l’instruction if-else et le code machine généré est toujours incertain. Les instructions suivantes sont les deux méthodes, et l’ancienne image n’est pas optimisée, la dernière est optimisée:

  • Instructions non optimisées: (Cliquez pour voir l’image en taille réelle.) ybhgM.png

  • Instructions optimisées: (Cliquez pour voir l’image en taille réelle.) 6kgzJ.png

Dans ce dernier cas, le bloc jaune est le code exécuté uniquement si i<=0 et le bloc bleu quand i>0 . Dans l'une ou l'autre version des instructions, l'instruction if-else est plus courte.

Notez que, pour différentes instructions, le [ CPI ] n'est pas nécessairement le même. Logiquement, pour l'instruction identique, plus d'instructions coûtent plus de temps. Mais si l'instruction de récupération de temps et de pipe / cache était également prise en compte, le temps réel total d'exécution dépend du processeur. Le processeur peut également prédire les twigs.

Les processeurs modernes ont encore plus de cœurs, les choses peuvent être plus complexes avec cela. Si vous étiez un utilisateur de processeur Intel, vous souhaiterez peut-être consulter [ Manuel de référence de l'optimisation des architectures Intel® 64 et IA-32 ].

Je ne sais pas s'il y avait un CLR implémenté dans le matériel, mais si oui, vous obtiendrez probablement un opérateur conditionnel plus rapide car l'IL est évidemment moindre.

Remarque: Tous les codes machine sont au format x86.

J’ai fait ce que Jon Skeet a fait et j’ai parcouru 1 itération et 1 000 itérations et j’ai obtenu un résultat différent à la fois de OP et de Jon. Dans le mien, le ternaire est juste un peu plus rapide. Voici le code exact:

 static void runIfElse(int[] array, int iterations) { long value = 0; Stopwatch ifElse = new Stopwatch(); ifElse.Start(); for (int c = 0; c < iterations; c++) { foreach (int i in array) { if (i > 0) { value += 2; } else { value += 3; } } } ifElse.Stop(); Console.WriteLine(Ssortingng.Format("Elapsed time for If-Else: {0}", ifElse.Elapsed)); } static void runTernary(int[] array, int iterations) { long value = 0; Stopwatch ternary = new Stopwatch(); ternary.Start(); for (int c = 0; c < iterations; c++) { foreach (int i in array) { value += i > 0 ? 2 : 3; } } ternary.Stop(); Console.WriteLine(Ssortingng.Format("Elapsed time for Ternary: {0}", ternary.Elapsed)); } static void Main(ssortingng[] args) { Random r = new Random(); int[] array = new int[20000000]; for (int i = 0; i < array.Length; i++) { array[i] = r.Next(int.MinValue, int.MaxValue); } Array.Sort(array); long value = 0; runIfElse(array, 1); runTernary(array, 1); runIfElse(array, 1000); runTernary(array, 1000); Console.ReadLine(); } 

La sortie de mon programme:

Temps écoulé pour If-Else: 00: 00: 00.0140543

Temps écoulé pour Ternary: 00: 00: 00.0136723

Temps écoulé pour If-Else: 00: 00: 14.0167870

Temps écoulé pour Ternary: 00: 00: 13.9418520

Une autre exécution en millisecondes:

Temps écoulé pour If-Else: 20

Temps écoulé pour Ternary: 19

Temps écoulé pour If-Else: 13854

Temps écoulé pour Ternary: 13610

Cela fonctionne sous XP 64 bits, et je me suis lancé sans débogage.

Edit - Exécution en x86:

Il y a une grande différence avec x86. Cela a été fait sans débogage sur et sur la même machine xp 64 bits qu'auparavant, mais conçu pour les processeurs x86. Cela ressemble plus à des OP.

Temps écoulé pour If-Else: 18

Temps écoulé pour Ternary: 35

Temps écoulé pour If-Else: 20512

Temps écoulé pour Ternary: 32673

Le code assembleur généré racontera l’histoire:

 a = (b > c) ? 1 : 0; 

Génère:

 mov edx, DWORD PTR a[rip] mov eax, DWORD PTR b[rip] cmp edx, eax setg al 

Tandis que:

 if (a > b) printf("a"); else printf("b"); 

Génère:

 mov edx, DWORD PTR a[rip] mov eax, DWORD PTR b[rip] cmp edx, eax jle .L4 ;printf a jmp .L5 .L4: ;printf b .L5: 

Donc, le ternaire peut être plus court et plus rapide simplement en utilisant moins d’instructions et pas de sauts si vous cherchez vrai / faux. Si vous utilisez des valeurs autres que 1 et 0, vous obtiendrez le même code que si / sinon, par exemple:

 a = (b > c) ? 2 : 3; 

Génère:

 mov edx, DWORD PTR b[rip] mov eax, DWORD PTR c[rip] cmp edx, eax jle .L6 mov eax, 2 jmp .L7 .L6: mov eax, 3 .L7: 

Ce qui est le même que si / sinon.

Exécuter sans déboguer Ctrl + F5, il semble que le débogueur ralentisse significativement les ifs et ternary, mais il semble que cela ralentisse beaucoup plus l’opérateur ternaire.

Lorsque je lance le code suivant, voici mes résultats. Je pense que la différence de la petite milliseconde est due au fait que le compilateur optimise le max = max et le supprime mais ne fait probablement pas cette optimisation pour l’opérateur ternaire. Si quelqu’un pouvait vérifier l’assemblage et confirmer cela, ce serait génial.

 --Run #1-- Type | Milliseconds Ternary 706 If 704 %: .9972 --Run #2-- Type | Milliseconds Ternary 707 If 704 %: .9958 --Run #3-- Type | Milliseconds Ternary 706 If 704 %: .9972 

Code

  for (int t = 1; t != 10; t++) { var s = new System.Diagnostics.Stopwatch(); var r = new Random(123456789); //r int[] randomSet = new int[1000]; //a for (int i = 0; i < 1000; i++) //n randomSet[i] = r.Next(); //dom long _ternary = 0; //store long _if = 0; //time int max = 0; //result s.Start(); for (int q = 0; q < 1000000; q++) { for (int i = 0; i < 1000; i++) max = max > randomSet[i] ? max : randomSet[i]; } s.Stop(); _ternary = s.ElapsedMilliseconds; max = 0; s = new System.Diagnostics.Stopwatch(); s.Start(); for (int q = 0; q < 1000000; q++) { for (int i = 0; i < 1000; i++) if (max > randomSet[i]) max = max; // I think the comstackr may remove this but not for the ternary causing the speed difference. else max = randomSet[i]; } s.Stop(); _if = s.ElapsedMilliseconds; Console.WriteLine("--Run #" + t+"--"); Console.WriteLine("Type | Milliseconds\nTernary {0}\nIf {1}\n%: {2}", _ternary, _if,((decimal)_if/(decimal)_ternary).ToSsortingng("#.####")); } 

En regardant l’IL généré, il y a 16 opérations de moins que dans l’instruction if / else (copier et coller le code de @ JonSkeet). Cependant, cela ne signifie pas que le processus devrait être plus rapide!

Pour résumer les différences dans IL, la méthode if / else se traduit à peu près comme le code C # lit (en effectuant l’ajout dans la twig) alors que le code conditionnel se charge sur 2 ou 3 sur la stack (en fonction de la valeur) puis l’ajoute à la valeur en dehors du conditionnel.

L’autre différence est l’instruction de twigment utilisée. La méthode if / else utilise une valeur brtrue (branch si true) pour sauter la première condition et une twig inconditionnelle pour sauter de la première des instructions if. Le code conditionnel utilise un bgt (twigment si supérieur à) au lieu d’un brtrue, ce qui pourrait éventuellement être une comparaison plus lente.

De plus (après avoir lu la prédiction de la twig), il peut y avoir une pénalité de performance pour la twig qui est plus petite. La twig conditionnelle n’a qu’une seule instruction dans la twig, mais if / else en a 7. Cela explique également pourquoi il existe une différence entre long et int, car le passage à un int réduit le nombre d’instructions dans les twigs if / else de 1. (rendant la lecture plus rapide)

Dans le code suivant, if / else semble être environ 1,4 fois plus rapide que l’opérateur ternaire. Cependant, j’ai constaté que l’introduction d’une variable temporaire diminue le temps d’exécution de l’opérateur ternaire d’environ 1,4 fois:

Si / Else: 98 ms

Ternaire: 141 ms

Ternaire avec temp var: 100 ms

 using System; using System.Diagnostics; namespace ConsoleApplicationTestIfElseVsTernaryOperator { class Program { static void Main(ssortingng[] args) { Random r = new Random(0); int[] array = new int[20000000]; for (int i = 0; i < array.Length; i++) { array[i] = r.Next(int.MinValue, int.MaxValue); } Array.Sort(array); long value; Stopwatch stopwatch = new Stopwatch(); value = 0; stopwatch.Restart(); foreach (int i in array) { if (i > 0) { value += 2; } else { value += 3; } // 98 ms } stopwatch.Stop(); Console.WriteLine("If/Else: " + stopwatch.ElapsedMilliseconds.ToSsortingng() + " ms"); value = 0; stopwatch.Restart(); foreach (int i in array) { value += (i > 0) ? 2 : 3; // 141 ms } stopwatch.Stop(); Console.WriteLine("Ternary: " + stopwatch.ElapsedMilliseconds.ToSsortingng() + " ms"); value = 0; int tempVar = 0; stopwatch.Restart(); foreach (int i in array) { tempVar = (i > 0) ? 2 : 3; value += tempVar; // 100ms } stopwatch.Stop(); Console.WriteLine("Ternary with temp var: " + stopwatch.ElapsedMilliseconds.ToSsortingng() + " ms"); Console.ReadKey(true); } } }