me encontré con una diferencia en la velocidad por medio de las siguientes dos estructuras:rendimiento constructor estático y por qué no podemos especificar beforefieldinit
public struct NoStaticCtor
{
private static int _myValue = 3;
public static int GetMyValue() { return _myValue; }
}
public struct StaticCtor
{
private static int _myValue;
public static int GetMyValue() { return _myValue; }
static StaticCtor()
{
_myValue = 3;
}
}
class Program
{
static void Main(string[] args)
{
long numTimes = 5000000000; // yup, 5 billion
Stopwatch sw = new Stopwatch();
sw.Start();
for (long i = 0; i < numTimes; i++)
{
NoStaticCtor.GetMyValue();
}
sw.Stop();
Console.WriteLine("No static ctor: {0}", sw.Elapsed);
sw.Restart();
for (long i = 0; i < numTimes; i++)
{
StaticCtor.GetMyValue();
}
sw.Stop();
Console.WriteLine("with static ctor: {0}", sw.Elapsed);
}
}
que produce los resultados:
Release (x86), no debugger attached:
No static ctor: 00:00:05.1111786
with static ctor: 00:00:09.9502592
Release (x64), no debugger attached:
No static ctor: 00:00:03.2595979
with static ctor: 00:00:14.5922220
El compilador produce una constructor estático para el NoStaticCtor
que es idéntico al declarado explícitamente en StaticCtor
. Entiendo que el compilador solo emitirá beforefieldinit
cuando un constructor estático no está explícitamente definido.
Producen casi idéntico código il, excepto por una diferencia, declarando la estructura con beforefieldinit
, que es donde me siento la diferencia radica ya sé que determina cuando el constructor de tipos se llama, aunque no acabo averiguar por qué hay tanta diferencia. Asume que no es llamando al el constructor de tipos en cada iteración, ya que un constructor de tipos solo se puede llamar una vez.
Así,
1) ¿Por qué la diferencia de tiempo entre la estructura con beforefieldinit
y el que no tiene? (I imaginar el JITer está haciendo algo extra en el bucle, sin embargo, no tengo idea de cómo ver la salida del JITer para ver qué.
2) ¿Por qué los diseñadores del compilador a) no hace que todas las estructuras beforefieldinit
ser predeterminado yb) no dar a los desarrolladores la capacidad de especificar explícitamente ese comportamiento? Por supuesto, este es asumiendo que no se puede, ya que no he podido encontrar la manera.
Editar:
. I modified the code, que se extiende esencialmente cada bucle por segunda vez, esperando una mejoría, pero no era mucho:
No static ctor: 00:00:03.3342359
with static ctor: 00:00:14.6139917
No static ctor: 00:00:03.2229995
with static ctor: 00:00:12.9524860
Press any key to continue . . .
he hecho porque yo, aunque, bueno, tal vez , por improbable que sea, la realidad era JITer llamando al constructor de tipos cada iteración. Me parece que el JITer sabría que el constructor de tipos ya ha sido llamado y no emitirá código para hacer eso cuando se compiló el segundo bucle.
Además de la respuesta de Motti: This code produce mejores resultados, debido a la diferencia en JITing, la JITing de DoSecondLoop
no emite el cheque ctor estático, ya que detecta que se hizo anteriormente en DoFirstLoop
, causando cada bucle para realizar a la misma velocidad (~ 3 segundos)
Aquí se requiere una cierta perspectiva de los números grandes. La sobrecarga que mediste es * un nanosegundo *. Sí, eso es lo que una instrucción test + jump toma. –
@Hans sé que la sobrecarga es pequeña. Realmente nunca escribiría un código como este usado en producción. Actualmente estoy en un período de "¿cómo funciona el CLR?" Y he estado jugando con cosas inusuales. Realmente estoy tratando de entender por qué el JITer toma las decisiones que toma, en función de los atributos que emite el compilador. Probablemente debería comprar un libro. –