Y at-il des risques pour optimiser le code en C #?

Dans le panneau des parameters de construction de VS2010 Pro, il y a une CheckBox portant le libellé “optimiser le code” … bien sûr, je veux le vérifier … mais étant exceptionnellement prudent, j’ai interrogé mon frère à ce sujet et il a répondu est décoché pour le débogage et qu’en C ++ il peut potentiellement faire des choses qui casseraient ou baliseraient le code … mais il ne sait pas à propos de C #.

Ma question est donc la suivante: puis-je cocher cette case pour ma version mise à jour sans craindre qu’elle ne casse mon code? Deuxièmement, si cela peut casser le code, quand et pourquoi? Liens vers des explications bienvenues.

Le CheckBox dont je parle.

Vous utiliseriez normalement cette option dans une version validée. C’est sécuritaire et ordinaire de le faire. Il n’y a aucune raison d’avoir peur de publier du code avec les optimisations activées. L’activation de l’optimisation peut interférer avec le débogage, ce qui constitue une bonne raison de la désactiver pour les versions de débogage.

Les optimisations ne devraient pas vraiment casser votre code. Eric Lippert a publié ici un article qui explique ce qui se passe lorsque vous activez ce drapeau. Le gain en performances varie d’une application à l’autre. Vous devez donc le tester avec votre projet pour voir s’il existe des différences notables (en termes de performances).

Lors de l’exécution en mode de publication, il est possible que certains bogues se produisent sans que cela se produise. On pense au fameux “drapeau non volatile”:

flag = false; Thread t = new Thread( o => { while(!flag) { // do stuff } }); t.Start(); // main thread does some work flag = true; t.Join(); // will never return in release mode if flag is not volatile 

Cela est dû aux optimisations du compilateur, car la variable flag est mise en cache par le kernel du thread t et ne peut donc pas voir la valeur mise à jour de flag.

Les optimisations doivent-elles introduire des bugs? Non.

Les optimisations pourraient-elles introduire des bugs? Peut-être que rien n’est parfait après tout.

Les optimisations pourraient-elles découvrir des bogues qui ont toujours été dans votre code, mais sont cachés lorsqu’ils sont désactivés? Absolument, ça arrive un peu.

L’important est de réaliser que c’est un changement. Tout comme si vous testiez si vous avez effectué beaucoup de modifications, vous devez tester lorsque vous les désactivez. Si la version finale les active, alors final-test doit également les activer.

En C #, l’optimisation ne doit JAMAIS détruire votre code.

Avec les optimisations activées, le compilateur produit un CIL plus compact lors de la traduction entre C # et CIL.

J’ai observé (et franchement, il est intéressant!) Que les compilateurs C # de .NET <2.0 (1.0 et 1.1) produisaient une CIL SANS optimisation aussi efficace que les compilateurs C # (2.0 et ultérieurs) produisaient AVEC des optimisations.

Exemple sage, j’ai un morceau de code de certaines parties de simulation de mon mémoire de maîsortingse. Dans lequel, lorsque l’indicateur d’optimisation est activé, le code ne rompt pas vraiment le programme, mais le pathfinder effectue uniquement une exécution et des boucles. (le code récursif se bloque dans une boucle du pathfinder dont il sort toujours avec l’indicateur d’optimisation désactivé).

Alors oui, il est possible que l’indicateur d’optimisation fasse en sorte que le logiciel se comporte différemment.

L’optimisation du compilateur .net pourrait causer des bogues. arrivé à moi aujourd’hui. m’a pris quelques heures pour le clouer. le code est:

 for (int i = 0; i < list.Count-1; i++) { list[i+1].DoSomeThing(); //some code if (someCondition) { list.insert(i+1, new Item()); i++; } } 

à un moment donné, la list[i+1] est adressée comme list[i] , comme si les deux pointaient vers le même élément. ce bug était tellement bizarre. le code fonctionnait bien en mode débogage et en mode libération, mais lorsque je l'ai exécuté côté visual studio, ex. à partir du fichier .exe, le code s'est écrasé. désactiver uniquement l'optimisation du compilateur a corrigé le problème.

Dans mon cas, lorsque le drapeau d’optimisations était activé, toutes les opérations n’étaient pas terminées. Il manquait donc des points de mesure dans le résultat final. J’ai donc simplement désactivé le drapeau d’optimisation pour corriger le bogue:

 using System.Threading.Tasks; Parallel.Invoke( async () => await ProcessPartialArrayOperationAssets(operationAssets, 0, operationAssets.Count / 2, operations, inspection1), async () => await ProcessPartialArrayOperationAssets(operationAssets, operationAssets.Count / 2, operationAssets.Count, operations, inspection1) ); private async Task ProcessPartialArrayInspectionOperations(IList operations, int begin, int end, Inspection inspection, InspectionAsset inspectionAsset) { await Task.Run(() => { // create one new operation measuring point for each measuring point in the operation's equipment int itemCounter = begin + 1; for (int i = begin; i < end; i++) { lock (_thisLock) { InspectionOperation operation = operations[i]; int itemNumber = 1; // get the asset InspectionAsset operationAsset = operation.OperationAsset; if (operationAsset != null) { // get the measuring points string ABAPTrue = Abap.ABAP_TRUE; lock (_thisLock) { IList measuringPoints = DbContext.MeasuringPoints.Where(x => x.AssetID == operationAsset.AssetID && x.InactiveFlag != ABAPTrue) .ToList(); if (measuringPoints != null) { //Debug.WriteLine("measuringPoints.Count = " + measuringPoints.Count); // create the operation measuring points foreach (MeasuringPoint measuringPoint in measuringPoints) { OperationMeasuringPoint operationMeasuringPoint = new OperationMeasuringPoint { InspectionID = inspection.InspectionID, OperationNumber = operation.OperationNumber, SubActivity = "", RoutingNo = "", ItemNumber = itemNumber.ToSsortingng("D4"), // eg "0001", "0002" and so on ItemCounter = itemCounter.ToSsortingng("D8"), // eg "00000001", "00000002" and so on MeasuringPointID = measuringPoint.MeasuringPointID, MeasuringPointDescription = measuringPoint.Description, Equipment = inspectionAsset.AssetID, Category = "P" }; DbContext.Entry(operationMeasuringPoint).State = EntityState.Added; itemNumber++; itemCounter++; } } } } } } }); } 

Ainsi, j’ai également remplacé l’appel Parallel.Invoke. Pour votre information, ce problème est survenu avec .NET Framework 4.7.

 await ProcessPartialArrayOperationAssets(operationAssets, 0, operationAssets.Count, operations, inspection1); 

METTRE À JOUR:

OK, j’ai constaté que je pouvais réactiver l’indicateur d’optimisation et utiliser Parallel.Invoke si je supprimais la async Task de la signature de la méthode:

  private void ProcessPartialArrayInspectionOperations(IList operations, int begin, int end, Inspection inspection, InspectionAsset inspectionAsset) { // create one new operation measuring point for each measuring point in the operation's equipment int itemCounter = begin + 1; for (int i = begin; i < end; i++) { InspectionOperation operation = operations[i]; int itemNumber = 1; // get the asset InspectionAsset operationAsset = operation.OperationAsset; if (operationAsset != null) { // get the measuring points string ABAPTrue = Abap.ABAP_TRUE; lock (_thisLock) { IList measuringPoints = DbContext.MeasuringPoints.Where(x => x.AssetID == operationAsset.AssetID && x.InactiveFlag != ABAPTrue) .ToList(); if (measuringPoints != null) { //Debug.WriteLine("measuringPoints.Count = " + measuringPoints.Count); // create the operation measuring points foreach (MeasuringPoint measuringPoint in measuringPoints) { OperationMeasuringPoint operationMeasuringPoint = new OperationMeasuringPoint { InspectionID = inspection.InspectionID, OperationNumber = operation.OperationNumber, SubActivity = "", RoutingNo = "", ItemNumber = itemNumber.ToSsortingng("D4"), // eg "0001", "0002" and so on ItemCounter = itemCounter.ToSsortingng("D8"), // eg "00000001", "00000002" and so on MeasuringPointID = measuringPoint.MeasuringPointID, MeasuringPointDescription = measuringPoint.Description, Equipment = inspectionAsset.AssetID, Category = "P" }; DbContext.Entry(operationMeasuringPoint).State = EntityState.Added; itemNumber++; itemCounter++; } } } } } } Parallel.Invoke( () => ProcessPartialArrayInspectionOperations(operations, 0, operations.Count / 2, inspection1, inspectionAsset), () => ProcessPartialArrayInspectionOperations(operations, operations.Count / 2, operations.Count, inspection1, inspectionAsset) ); 

Alternativement, je pense que je pourrais utiliser Task.Run pour chacun puis attendre une Task.WhenAll(t1, t2, t3); comme expliqué ici, mais dans ce cas, je ne fais pas d’appels de firebase database explicites, donc je ne pense pas qu’il soit Task.Run utiliser Task.Run place de Parallel.Invoke bien que cette page explique pourquoi mon Parallel.Invoke n’a pas été complété: n’attend pas la fin des méthodes asynchrones

Pour plus de détails, voir “Concurrence en C #” https://stephencleary.com/book/.