Est-ce que mon compilateur ignorera le code inutile?

j'ai été à travers quelques questions sur le réseau à ce sujet, mais je n'ai pas trouvé de réponse à ma question, ou c'est pour une autre langue ou il ne fait pas réponse totalement (code mort est pas code inutile) donc voici ma question:

est-ce que (explicite ou non) le code inutile est ignoré par le compilateur?

par exemple, dans ce code:

double[] TestRunTime = SomeFunctionThatReturnDoubles;
// A bit of code skipped
int i = 0;
for (int j = 0; j < TestRunTime.Length; j++)
{

}
double prevSpec_OilCons = 0;

la boucle for-ils être supprimés?

j'utilise et


le fond est que je maintiens beaucoup de code (que je n'ai pas écrit) et je me demandais si le code inutile devrait être une cible ou si je pouvais laisser le compilateur prendre soin de cela.

40
demandé sur Community 2015-06-24 15:49:41

9 réponses

bien, vos variables i et prevSpec_OilCons , si elles ne sont utilisées nulle part, seront optimisées à l'extérieur, mais pas votre boucle.

donc si votre code ressemble à:

static void Main(string[] args)
{
    int[] TestRunTime = { 1, 2, 3 };
    int i = 0;
    for (int j = 0; j < TestRunTime.Length; j++)
    {

    }
    double prevSpec_OilCons = 0;
    Console.WriteLine("Code end");
}

sous ILSpy il sera:

private static void Main(string[] args)
{
    int[] TestRunTime = new int[]
    {
        1,
        2,
        3
    };
    for (int i = 0; i < TestRunTime.Length; i++)
    {
    }
    Console.WriteLine("Code end");
}

étant donné que la boucle comporte deux énoncés, comme comparaison et incrément, elle pourrait être utilisée pour mettre en œuvre quelque peu court délai/période d'attente. (bien que ce ne soit pas une bonne pratique) .

Envisager la boucle suivante, qui est un vide boucle, mais il faudra beaucoup de temps pour s'exécuter.

for (long j = 0; j < long.MaxValue; j++)
{

}

La boucle dans votre code, n'est pas un code mort, dans la mesure du code mort, le suivant est un code mort, et sera optimisé loin.

if (false)
{
    Console.Write("Shouldn't be here");
}

la boucle, ne sera même pas supprimé par le .NET jitters. Sur la base de ce réponse

27
répondu Habib 2017-05-23 12:34:59

La boucle ne peut pas être supprimé , le code est pas mort , par exemple:

  // Just some function, right?
  private static Double[] SomeFunctionThatReturnDoubles() {
    return null;
  }

  ...
  double[] TestRunTime = SomeFunctionThatReturnDoubles();
  ...
  // You'll end up with exception since TestRunTime is null
  for (int j = 0; j < TestRunTime.Length; j++)
  {
  }
  ...

habituellement, le compilateur juste ne peut pas prédire tous les résultats possibles de SomeFunctionThatReturnDoubles et c'est pourquoi il préserve la boucle

15
répondu Dmitry Bychenko 2015-06-24 14:11:16

dans votre boucle il y a deux opérations implicites dans chaque itération. Un accroissement:

j++;

et comparaison

j<TestRunTime.Length;

ainsi, la boucle n'est pas vide bien qu'elle semble l'être. Il y a quelque chose qui est exécuté à la fin et cela n'est pas ignoré par le compilateur bien sûr.

cela se produit aussi dans d'autres boucles.

6
répondu Fleve 2015-06-24 12:54:08

il ne sera pas ignoré. Cependant, quand vous arrivez à IL il y aura une déclaration de saut, donc le for s'exécutera comme si c'était une déclaration de si. Il lancera également le code pour ++ et length, comme @Fleve l'a mentionné. Ce sera juste du code supplémentaire. Pour des raisons de lisibilité, ainsi que pour respecter les normes du code, je retirerais le code si vous ne l'utilisez pas.

4
répondu Bob 2015-06-24 12:56:07

est-ce que (explicite ou non) le code inutile est ignoré par le compilateur?

vous ne pouvez pas facilement déterminer qu'il est inutile, donc le compilateur ne peut pas non plus. Le getter de TestRunTime.Length peut avoir des effets secondaires, par exemple.

le fond est que je maintiens beaucoup de code (que je n'ai pas écrit) et je me demandais si le code inutile devrait être une cible

morceau de code, vous devez vérifier ce qu'il fait afin d'être en mesure de le changer et dire après il a toujours le même résultat. Les tests unitaires sont une excellente façon de le faire.

4
répondu CodeCaster 2015-06-24 13:06:52

le JIT est essentiellement capable de supprimer le code mort. Il n'est pas très approfondie. Les variables et les expressions mortes sont tuées de façon fiable. C'est une optimisation facile dans la forme SSA.

pas sûr du débit de contrôle. Si vous faites deux boucles, seule la boucle intérieure sera effacée.

Si vous voulez savoir ce qui est supprimé et ce n'est pas regarder l'généré x86 code. Le compilateur C# effectue très peu d'optimisations. Le JIT en fait quelques-uns.

les JITs de 4.5 32 et 64 bits sont des bases de code différentes et ont un comportement différent. Un nouveau JIT (RyuJIT) est à venir qui dans mes essais fait généralement pire, parfois mieux.

4
répondu usr 2015-06-24 13:07:07

évidemment votre compilateur ne va pas ignorer code inutile, mais analysez-le soigneusement et ensuite essayer de le supprimer, si elle effectue des optimisations.

Dans votre cas, la première chose intéressante est de savoir si la variable j est utilisée après la boucle ou pas. L'autre chose intéressante est TestRunTime.Longueur. Le compilateur va regarder et vérifier si elle retourne toujours le même résultat, et si oui si il a des effets secondaires, et si oui si l'appeler une fois a le même effet secondaire au total que l'appeler à plusieurs reprises.

Si TestRunTime.La longueur n'a pas d'effet secondaire et j n'est pas utilisée, alors la boucle est supprimé.

Sinon, si vous appelez TestRunTime.Length repeatedly a plus d'effets secondaires que de l'appeler une fois, ou si des appels répétés renvoient des valeurs différentes, alors la boucle doit être exécutée.

sinon, j = max (0, TestRunTime.Longueur.)

suivant, le compilateur peut déterminer si la tâche TestRunTime.La longueur est nécessaire. Il peut être remplacé par du code qui détermine juste ce que TestRunTime.La longueur serait.

alors bien sûr votre compilateur pourrait ne pas essayer des optimisations fantaisistes, ou les règles de langue pourraient être de sorte qu'il ne peut pas déterminer ces choses, et vous êtes coincé.

4
répondu gnasher729 2015-06-24 16:35:40

pour la plupart, vous ne devriez pas vous soucier de supprimer le code inutile de façon proactive. Si vous rencontrez des problèmes de performance, et que votre profileur dit qu'un code inutile mange vos cycles d'horloge, alors allez nucléaire. Cependant, si le code ne fait vraiment rien et n'a pas d'effets secondaires, alors il aura probablement peu d'impact sur la durée de fonctionnement.

cela dit, la plupart des compilateurs ne sont pas tenus d'effectuer des optimisations, de sorte que compter sur des optimisations de compilateurs n'est pas toujours l'option la plus intelligente. Dans de nombreux cas cependant, même une boucle de spin inutile peut s'exécuter assez rapidement. Un spinlock de base qui fait des boucles un million de fois se compilerait en quelque chose comme mov eax, 0 \ inc eax \ cmp eax, 1000000 \ jnz -8 . Même si nous avons exclu les optimisations on-CPU, cela ne fait que 3 cycles par boucle (sur une puce récente de style RISC) puisqu'il n'y a pas d'accès mémoire, donc il n'y aura pas d'invalidation de cache. Sur un CPU 1GHz, c'est seulement 3 000 000/1 000 000 000 de secondes, soit 3 millisecondes. Ce serait un coup assez significatif si vous essayé de courir à 60 fois par seconde, mais dans de nombreux cas, il ne sera probablement pas perceptible.

une boucle comme celle que j'ai décrite serait presque certainement optimisée en mov eax 1000000 , même dans un environnement JIT. Il serait probablement optimisé plus loin que cela, mais en l'absence d'autre contexte, que l'optimisation est raisonnable et ne causerait pas d'effets néfastes.

tl;dr: si votre profiler dit que le code mort / inutile utilise une quantité appréciable de votre au moment de l'exécution des ressources, de la supprimer. Ne pas aller sur une chasse aux sorcières pour le code mort cependant; laisser que pour la réécriture/réfacteur massif le long de la ligne.

Bonus: si le générateur de code savait que eax ne serait pas lu pour autre chose que la condition de boucle et voulait garder le spinlock, il pourrait générer mov eax, 1000000 \ dec eax \ jnz -3 et réduire la pénalité de la boucle d'un cycle. La plupart des compilateurs le supprimeraient complètement.

3
répondu Kaslai 2015-06-24 17:23:07

j'ai fait un petit formulaire pour le tester selon quelques idées de réponses sur l'utilisation de long.MaxValue , voici mon code de référence:

public Form1()
{
    InitializeComponent();
    Stopwatch test = new Stopwatch();
    test.Start();
    myTextBox.Text = test.Elapsed.ToString();
}

et voici le code avec en quelque sorte code inutile:

public Form1()
{
    InitializeComponent();
    Stopwatch test = new Stopwatch();
    test.Start();
    for (int i = 0; i < int.MaxValue; i++)
    {
    }
    myTextBox.Text = test.Elapsed.ToString();
}

vous remarquerez que j'ai utilisé int.MaxValue au lieu de long.MaxValue , Je ne voulais pas passer le année jour sur celui-ci.

Comme vous pouvez le voir:

---------------------------------------------------------------------
|                   |   Debug               |   Release             |
---------------------------------------------------------------------
|Ref                |   00:00:00.0000019    |   00:00:00.0000019    |
|Useless code       |   00:00:05.3837568    |   00:00:05.2728447    |
---------------------------------------------------------------------

le code n'est pas optimisé. Attends un peu, je vais essayer avec quelques int[] pour tester int[].Lenght :

public Form1()
{
    InitializeComponent();
    int[] myTab = functionThatReturnInts(1);

    Stopwatch test = new Stopwatch();
    test.Start();
    for (int i = 0; i < myTab.Length; i++)
    {

    }
    myTextBox.Text = test.Elapsed.ToString();
}
public int[] functionThatReturnInts(int desiredSize)
{
    return Enumerable.Repeat(42, desiredSize).ToArray();
}

et voici les résultats:

---------------------------------------------
|   Size            |   Release             |
---------------------------------------------
|             1     |   00:00:00.0000015    |
|           100     |   00:00:00            |
|        10 000     |   00:00:00.0000035    |
|     1 000 000     |   00:00:00.0003236    |
|   100 000 000     |   00:00:00.0312673    |
---------------------------------------------

donc même avec les tableaux, il n'est pas optimisé du tout.

1
répondu Thomas Ayoub 2015-12-10 15:55:04