Edit :
Ajout d'un exemple qui peut être fait avec l'instruction if-else mais pas avec l'opérateur conditionnel.
Avant de répondre, veuillez jeter un coup d'oeil à [ Lequel est le plus rapide ? ] sur le blog de M. Lippert. Et je pense Réponse de M. Ersönmez est le plus correct 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 aussi performant que l'instruction if-else. en C .
La raison en est simple : et si l'instruction if-else n'est pas utilisée ?
if (i > 0)
{
value += 2;
}
else
{
}
L'exigence de l'opérateur conditionnel est il doit y avoir une valeur avec l'un ou l'autre côté, et en C il faut aussi que les deux côtés de :
a le même type. Cela la rend simplement différente 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 le cas :
Quelle que soit l'expression évaluée, il y a une valeur.
Mais avec l'instruction if-else :
Si l'expression est évaluée comme vraie, faites quelque chose ; sinon, faites 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 les deux serait le suivant :
var array1=new[] { 1, 2, 3 };
var array2=new[] { 5, 6, 7 };
if(i>0)
array1[1]=4;
else
array2[2]=4;
le code ci-dessus compile, cependant, remplacer l'instruction if-else par l'opérateur conditionnel ne compile pas :
var array1=new[] { 1, 2, 3 };
var array2=new[] { 5, 6, 7 };
(i>0?array1[1]:array2[2])=4; // incorrect usage
L'opérateur conditionnel et les instructions if-else sont des concepts identiques lorsque vous faites la même chose, cela peut même être plus rapide avec l'opérateur conditionnel. en C puisque C est plus proche de l'assemblage de la plate-forme.
Dans le code original que vous avez fourni, l'opérateur conditionnel est utilisé dans une boucle foreach, ce qui rendrait difficile de voir la différence entre les deux. 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 de optimisé et non. Comme elles sont longues, j'utilise une image pour les montrer, la partie droite est la version optimisée :
(Cliquez pour voir l'image en taille réelle.)
Dans les deux versions du code, le IL de l'opérateur conditionnel semble plus court que l'instruction if-else, et il y a toujours un doute sur le code machine finalement généré. Voici les instructions des deux méthodes, la première image est non optimisée, la seconde est optimisée :
Dans ce dernier, le bloc jaune est le code qui n'est exécuté que si i<=0
et le bloc bleu est lorsque i>0
. Dans les deux versions d'instructions, l'instruction if-else est plus courte.
Notez que, pour différentes instructions, les [ IPC ] n'est pas nécessairement la même chose. Logiquement, pour une instruction identique, plus d'instructions coûtent un cycle plus long. Mais si le temps de récupération des instructions et le pipe/cache sont également pris en compte, alors le temps total réel d'exécution dépend du processeur. Le processeur peut également prédire les branches.
Les processeurs modernes ont encore plus de cœurs, les choses peuvent être plus complexes avec cela. Si vous utilisez un processeur Intel, vous devriez jeter un coup d'oeil à [ Manuel de référence sur l'optimisation des architectures Intel® 64 et IA-32 ].
Je ne sais pas s'il existait un CLR implémenté par le matériel, mais si c'est le cas, vous serez probablement plus rapide avec un opérateur conditionnel car l'IL est évidemment moindre.
Note : Tous les codes machine sont de type x86.
96 votes
Première chose à corriger : n'utilisez pas
DateTime
pour mesurer la performance. UtilisezStopwatch
. Ensuite, le temps plutôt long - c'est un temps très court à mesurer.2 votes
J'ai essayé de le boucler 10 fois de plus, avec une graine de 42, j'ai obtenu 849 ms et 1564 ms. Je ne pense pas qu'il s'agisse d'une erreur de mesure.
49 votes
Utilisez une graine lorsque vous créez le
Random
de sorte qu'il donne toujours la même séquence. Si vous testez un code différent avec des données différentes, vous pouvez très bien constater des différences de performances.12 votes
Avez-vous également essayé de le compiler/exécuter en mode release avec les optimisations du compilateur activées, et sans débogueur ?
2 votes
Utiliser une application console plutôt que ce qui ressemble à une application WinForms serait également une bonne idée...
2 votes
Pourquoi avez-vous trié votre tableau ? Quels résultats obtenez-vous si vous ne le faites pas ?
2 votes
SO-Aide : Vous ne devez poser que des questions pratiques, auxquelles il est possible de répondre, basées sur des problèmes réels auxquels vous êtes confrontés. Alors, quel problème essayez-vous de résoudre grâce aux performances ?
7 votes
@LarryOBrien : Intéressant. Je viens de faire un test rapide avec LINQPad et j'obtiens des résultats très différents avec le tableau trié ou non. En fait, avec le tri, je reproduis la même différence de vitesse. La suppression du tri élimine également la différence de temps.
1 votes
Il existe de nombreuses optimisations possibles du compilateur pour votre code qui affecteront son temps d'exécution et fausseront les résultats. (Le tableau étant trié pour l'un, la vérification du signe de la valeur, l'addition systématique de deux nombres).
0 votes
Pour info, il n'y a pas de différence sous Mono sur Mac : larryobrien$ csharp tnry.cs Elapsed 00:00:00.0637078 larryobrien$ csharp tnry.cs Elapsed 00:00:00.0631514
39 votes
Le point ici est que le test de performance des micro-optimisations est dur . Pratiquement toutes les choses que vous observez dans votre résultat sont liées à des bogues dans votre code de test, et non à des différences dans le code significatif. Lorsque vous aurez corrigé celles qui sont listées ici, il y en aura d'autres, je peux vous l'assurer. La morale de l'histoire, c'est qu'il ne faut pas s'embêter avec les micro-optimisations ou essayer de les tester en premier lieu. Si le code est réellement difficile à mesurer, cela signifie qu'il n'est pas assez lent pour être un goulot d'étranglement ; ignorez-le.
4 votes
Je soupçonne fortement que cela a à voir avec cette réponse ici : stackoverflow.com/q/11227809/109122 . (Ce qui est probablement la question et la réponse la mieux notée sur SO.)
0 votes
@RBarryYoung Je ne pense pas. C'est un tableau trié dans les deux des tests. Il ne compare jamais des séries triées avec des séries non triées.
0 votes
@Servy Regardez sa réponse à Larry OBrien ci-dessus.
0 votes
@ChrisSinclair D'après la réponse de Skeet, la raison pour laquelle le cas non trié ne montre pas de différence de temps est que les mauvaises prédictions des branches sont si coûteuses qu'elles cachent probablement la différence de minutes.
0 votes
Essayez de l'exécuter en utilisant
(i<0)?(value+=2):(value+=3)
.0 votes
La réponse de @280Z28 est la meilleure - sans contestation - mais vous devriez toujours envisager d'éliminer la nécessité d'une conditionnelle lorsqu'il s'agit de performances. JavaScript est faiblement typé, vous pouvez donc modifier
value += i > 0 ? 2 : 3;
àvalue += (i > 0) + 2;
. Que diriez-vous de l'assemblage pour cela, @280Z28 ?