Si abre esta función en el depurador, con el código compilado en modo de depuración:
bool foo(string arg)
{
return bar(arg);
}
Hay 3 puntos de quiebre que puede establecer:
- En la llave de apertura de la función.
- En la línea de "devolución".
- En la llave de cierre de la función.
Establecer un punto de interrupción en la abrazadera de apertura significa "romper cuando se llame a esta función". Es por eso que hay una instrucción de no-operación al comienzo del método. Cuando el punto de interrupción se establece en la llave de apertura, el depurador realmente lo establece en la operación no operativa.
Establecer un punto de interrupción en la abrazadera de cierre significa "romper cuando esta función finaliza". Para que esto suceda, la función necesita tener una sola instrucción de retorno en su IL, donde se puede establecer el punto de ruptura. El compilador permite que mediante el uso de una variable temporal para almacenar el valor de retorno, y la conversión de
return retVal;
en
$retTmp = retVal;
goto exit;
y luego inyectar el siguiente código en la parte inferior del método:
exit:
return $ret;
Además, cuando se encuentran en modo de depuración, los compiladores son estúpidos con respecto al código que generan.Básicamente, hacer algo como:
GenerateProlog();
foreach (var statement in statements)
{
Generate(statement);
}
GenerateEpilog();
En su caso, que está viendo:
return foo(arg);
que se traduce en:
; //this is a no-op
bool retTemp = false;
retTemp = foo(arg);
goto exit;
exit:
return retTemp;
Si el compilador estaba haciendo una "optimización de ventana deslizante" podría ser capaz de mirar ese código y darse cuenta de que había cierta redundancia. Sin embargo, los compiladores generalmente no lo hacen en modo de depuración. Las optimizaciones del compilador pueden hacer cosas como eliminar variables y reordenar instrucciones, lo que dificulta la depuración. Como el propósito de una compilación de depuración es habilitar la depuración, no sería bueno activar las optimizaciones.
En una compilación de versión, el código no se verá así. Esto se debe a que el compilador no introduce el código especial para permitir puntos de interrupción en las llaves de apertura y cierre, que sólo deja la siguiente para ser compilado:
return bar(arg);
que termina pareciendo bastante simple.
Una cosa a tener en cuenta, sin embargo, es que no creo que el compilador C# haga muchas optimizaciones de ventanas deslizantes, incluso en construcciones minoristas. Eso es porque la mayoría de esas optimizaciones dependen de la arquitectura del procesador subyacente y, por lo tanto, las realiza el compilador JIT. Hacer las optimizaciones, incluso las que son independientes del procesador, en el compilador de C# puede impedir la capacidad del JIT de optimizar el código (busca patrones generados por la generación de código no optimizado, y si ve una IL altamente optimizada, puede obtener confuso). Por lo general, los compiladores de código gestionados no los hacen. Hace algunas "cosas costosas" (que el JIT no quiere hacer en tiempo de ejecución), como la detección de código muerto y el análisis de variables activas, pero no resuelven los problemas resueltos mediante la optimización de ventanas deslizantes.
suena razonable para mí –