.net clr jit inlining

.net - ¿Cuándo es un método elegible para ser incluido por el CLR?



jit inlining (4)

A pesar de la respuesta de Hans Passant, here primero un par de sugerencias a partir de 2004, y más abajo, información más actualizada. Están sujetos a cambios, pero le dan una idea sobre qué buscar si desea que un método sea elegible para la inclusión:

el JIT no se alineará:

  • Métodos marcados con MethodImplOptions.NoInlining
  • Métodos de más de 32 bytes de IL
  • Métodos virtuales
  • Métodos que toman un tipo de valor grande como parámetro
  • Métodos en las clases de MarshalByRef
  • Métodos con flowgraphics complicados
  • Métodos que cumplen otros criterios más exóticos

En particular, está MethodImplOptions.AggressiveInlining , que se supone que eleva el límite de 32 bytes (o lo que sea en estos días y para su plataforma).

.Net 3.5 agregó heurísticas que lo ayudan a determinar si blogs.msdn.com/b/vancem/archive/2008/08/19/… , lo cual es probablemente una buena cosa, aunque hace que sea más difícil para el desarrollador predecir la decisión de la inestabilidad:

Una cita del artículo:

  1. Si la línea interna hace que el código sea más pequeño que la llamada que reemplaza, SIEMPRE es bueno. Tenga en cuenta que estamos hablando del tamaño del código NATIVO, no del tamaño del código IL (que puede ser bastante diferente).

  2. Cuanto más se ejecuta un sitio de llamada en particular, más se beneficiará de la inserción. Por lo tanto, los bucles de códigos de entrada merecen estar más enlistados que los códigos que no están en bucles.

  3. Si la línea de entrada expone optimizaciones importantes, entonces la incorporación es más deseable. En particular, los métodos con argumentos de tipos de valor se benefician más de lo normal debido a optimizaciones como esta y, por lo tanto, tener un sesgo para alinear estos métodos es bueno.

Por lo tanto, la heurística que utiliza el compilador X86 JIT es dada un candidato en línea.

  1. Calcule el tamaño del sitio de llamadas si el método no está en línea.

  2. Estime el tamaño del sitio de la llamada si estuviera en línea (esta es una estimación basada en la IL, empleamos una máquina de estado simple (Modelo de Markov), creada usando muchos datos reales para formar esta lógica de estimador)

  3. Calcule un multiplicador. Por defecto es 1

  4. Aumente el multiplicador si el código está en un bucle (la heurística actual lo topa a 5 en un bucle)

  5. Aumenta el multiplicador si parece que las optimizaciones struct entrarán en acción.

  6. Si InlineSize <= NonInlineSize * Multiplicador haga la línea entrante.

He observado una gran cantidad de código "stack-introspectivo" en las aplicaciones, que a menudo implícitamente confían en que sus métodos de contención no estén en línea para su corrección. Tales métodos comúnmente implican llamadas a:

  • MethodBase.GetCurrentMethod
  • Assembly.GetCallingAssembly
  • Assembly.GetExecutingAssembly

Ahora, encuentro que la información que rodea estos métodos es muy confusa. He oído que el tiempo de ejecución no se alineará con un método que llama a GetCurrentMethod, pero no puedo encontrar ninguna documentación para ese efecto. He visto publicaciones en StackOverflow en varias ocasiones, como esta , que indica que CLR no realiza llamadas de ensamblaje cruzado en línea, pero la documentation GetCallingAssembly indica lo contrario.

También está el muy difamado [MethodImpl(MethodImplOptions.NoInlining)] , pero no estoy seguro si el CLR considera que se trata de una "solicitud" o un "comando".

Tenga en cuenta que estoy preguntando sobre la inclusión de la elegibilidad desde el punto de vista del contrato, no sobre cuándo las implementaciones actuales de JITter se niegan a considerar los métodos debido a dificultades de implementación, o sobre cuándo JITter finalmente elige elegir alinear un método elegible después de evaluar el comercio. offs. He leído this y this , pero parecen estar más centrados en los dos últimos puntos (hay menciones pasajeras de MethodImpOptions.NoInlining y "instrucciones exóticas de IL", pero parecen presentarse como heurísticas más que como obligaciones ).

¿Cuándo se permite el CLR en línea?


Es un detalle de implementación de jitter, los nervios x86 y x64 tienen reglas sutilmente diferentes. Esto está casualmente documentado en las publicaciones de blog de los miembros del equipo que trabajaron en el jitter, pero los equipos ciertamente se reservan el derecho de alterar las reglas. Parece que ya los encontraste.

Los métodos de enlining de otras asambleas son ciertamente apoyados, muchas de las clases de .NET funcionarían bastante miserablemente si ese no fuera el caso. Puede verlo en funcionamiento cuando mira el código máquina generado para Console.WriteLine (), a menudo se inline cuando pasa una cadena simple. Para verlo usted mismo, debe cambiar a la versión Release y cambiar la opción del depurador. Herramientas + Opciones, Depuración, General, desmarque "Suprimir la optimización de JIT en la carga del módulo".

De lo contrario, no hay una buena razón para considerar MethodImpOptions.NoInline difamado, es más o menos por qué existe en primer lugar. De hecho, se usa intencionalmente en el marco de .NET en muchos métodos públicos pequeños que llaman a un método interno de ayuda. Hace que los rastreos de pila de excepción sean más fáciles de diagnosticar.


Hay más información sobre la incorporación de MethodBase.GetCurrentMethod en este hilo http://prdlxvm0001.codify.net/pipermail/ozdotnet/2011-March/009085.html

Parafraseando en gran medida, indica que RefCrawlMark NO detiene el método de llamada que está en línea. Sin embargo, RequireSecObject tiene el efecto secundario de detener la llamada del invocador.

Además, los métodos Assembly.GetCallingAssembly y Assembly.GetExecutingAssembly NO tienen este atributo.