Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

220
Views
¿Por qué no se puede optimizar la verificación de campo de solo lectura fuera de un bucle?

Cuando tengo una variable de solo lectura:

 public readonly bool myVar = true;

Y compruébalo en un código como este:

 for(int i = 0; i != 10; i++) { if(myVar) DoStuff(); else DoOtherStuff(); }

Al observar la IL emitida, puedo ver que la verificación se realiza en cada iteración del ciclo. Esperaría que el código anterior produzca el mismo IL que este:

 if (myVar) { for(int i = 0; i != 10; i++) { DoStuff(); } } else { for(int i = 0; i != 10; i++) { DoOtherStuff(); } }

Entonces, ¿por qué la verificación no está optimizada para el exterior del ciclo, ya que el campo es de solo lectura y no se puede cambiar entre iteraciones?

over 4 years ago · Santiago Trujillo
3 answers
Answer question

0

Porque se puede cambiar usando Reflection:

 using System; using System.Reflection; public class Program { public static void Main() { var t = new Test(); var field = typeof(Test).GetField("myVar", BindingFlags.Instance | BindingFlags.Public); Console.WriteLine(t.myVar); // prints True field.SetValue(t, false); Console.WriteLine(t.myVar); // prints False // Trying to use t.myVar = false or true; <-- does not compile } } public class Test { public readonly bool myVar = true; }

Violín de trabajo: https://dotnetfiddle.net/W9UO3m

Tenga en cuenta que no hay forma en que el optimizador de código de tiempo de compilación pueda predecir o detectar con absoluta certeza si dicho código de reflexión existe o no, y si existe, si se ejecutará o no.

over 4 years ago · Santiago Trujillo Report

0

Su optimización propuesta realmente es una combinación de dos transformaciones individuales más simples. Lo primero es sacar el acceso del miembro fuera del bucle. Desde

 for(int i = 0; i != 10; i++) { var localVar = this.memberVar; if(localVar) DoStuff(); else DoOtherStuff(); }

para

 var localVar = this.memberVar; for(int i = 0; i != 10; i++) { if(localVar) DoStuff(); else DoOtherStuff(); }

El segundo es intercambiar la condición de bucle con la condición if. Desde

 var localVar = this.memberVar; for(int i = 0; i != 10; i++) { if(localVar) DoStuff(); else DoOtherStuff(); }

para

 var localVar = this.memberVar; if (localVar) { for(int i = 0; i != 10; i++) DoStuff(); } else { for(int i = 0; i != 10; i++) DoOtherStuff(); }

El primero está influenciado por readonly . Para hacerlo, el compilador tiene que demostrar que la memberVar no puede cambiar dentro del ciclo y readonly garantiza este 1 , aunque este ciclo se puede llamar dentro de un constructor y el valor de la memberVar se puede cambiar en el constructor después de que finaliza el ciclo. , no se puede cambiar en el cuerpo del bucle: DoStuff() no es un constructor del objeto actual, ni tampoco lo es DoOtherStuff() . Reflection no cuenta, aunque es posible usar Reflection para romper invariantes, no está permitido hacerlo. Los hilos cuentan, ver nota al pie.

La segunda es una transformación simple pero una decisión más difícil de tomar para el compilador, porque es difícil predecir si realmente mejorará el rendimiento. Naturalmente, puede verlo por separado haciendo la primera transformación en el código usted mismo y viendo qué código se genera.

Quizás una consideración más importante es que en .NET, el paso de optimización se lleva a cabo entre MSIL y el código de máquina, no durante la compilación de C# a IL. ¡Así que no puede ver qué optimizaciones se están haciendo mirando el MSIL!


1 ¿O sí? El modelo de memoria de .NET es considerablemente más indulgente que, por ejemplo, el modelo de C++, donde cualquier carrera de datos conduce muy rápidamente a un comportamiento indefinido a menos que el objeto se defina como volatile /atómico. ¿Qué sucede si este bucle se ejecuta en un subproceso de trabajo generado por el constructor de objetos y, después de generar el subproceso, el constructor continúa (lo que llamaré la "segunda mitad") para cambiar el miembro de readonly ? ¿El modelo de memoria requiere que el subproceso de trabajo vea ese cambio? ¿Qué pasa si DoStuff() y la segunda mitad del constructor fuerzan vallas de memoria, por ejemplo, acceden a otros miembros que son volatile o toman un bloqueo? Entonces , solo readonly solo permitiría la optimización en un entorno de un solo subproceso .

over 4 years ago · Santiago Trujillo Report

0

Un campo de readonly se puede inicializar con diferentes valores en diferentes puntos del código (múltiples constructores). No se puede optimizar porque múltiples constructores permiten múltiples valores del campo en cuestión. Ahora, si solo tuviera un constructor, sin bifurcaciones relevantes y, por lo tanto, solo un valor posible para ese campo, esperaría más del optimizador. Sin embargo, ese tipo de análisis es una de las razones por las que C++ tarda mucho más en compilarse que C#, y este tipo de tareas normalmente no se delegan al tiempo de ejecución.

Para una explicación más clara:

Nota

La palabra clave readonly es diferente de la palabra clave const. Un campo const solo se puede inicializar en la declaración del campo. Un campo de solo lectura se puede asignar varias veces en la declaración del campo y en cualquier constructor. Por lo tanto, los campos de solo lectura pueden tener diferentes valores según el constructor utilizado. Además, mientras que un campo const es una constante de tiempo de compilación, el campo de solo lectura se puede usar para constantes de tiempo de ejecución como en el siguiente ejemplo:

Consulte https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/readonly

Para su caso específico, sugeriría usar const en lugar de readonly , aunque admito que no he buscado diferencias en la IL generada.

Honestamente, me pregunto si realmente hace una diferencia. Cualquier CPU que realice una predicción de bifurcación y tenga un caché medianamente decente probablemente producirá el mismo rendimiento en cualquier caso. (Tenga en cuenta que no lo he probado ; es solo una sospecha).

over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!