una sola programacion pasada lenguaje historia compilador caracteristicas c# optimization compilation resharper

sola - ¿El compilador y la optimización de C#romperán este código?



lenguaje c# pdf (3)

Dado el siguiente código C # dentro de una función:

.... var documentCollection = client.CreateDocumentCollectionQuery("dbs/" + database.Id) .Where(c => c.Id == DocumentCollectionName) .AsEnumerable() .FirstOrDefault(); if (documentCollection == null) { documentCollection = await client.CreateDocumentCollectionAsync( "dbs/" + database.Id, new DocumentCollection { Id = DocumentCollectionName }); } return client;

Nota: no estoy devolviendo documentCollection , solo necesito que se inicialice, si no lo está ya (la llamada CreateDocumentCollectionAsync ). Entonces, después del bloque if , documentCollection convierte en una variable no utilizada.

Ahora - ReSharper propone optimizar esto para:

var documentCollection = client.CreateDocumentCollectionQuery("dbs/" + database.Id) .Where(c => c.Id == DocumentCollectionName) .AsEnumerable() .FirstOrDefault() ?? await client.CreateDocumentCollectionAsync( "dbs/" + database.Id, new DocumentCollection { Id = DocumentCollectionName });

E indica ahora que documentCollection es una variable no utilizada.

Mi pregunta: ¿la optimización del código C # o una compilación de ''lanzamiento'' eliminarán completamente esta línea de código y CreateDocumentCollectionAsync como resultado que CreateDocumentCollectionAsync nunca se CreateDocumentCollectionAsync ?

El curso de optimización de C # me enseñó que las compilaciones "liberadas" generan variables de recolección de basura tan pronto como no son necesarias en la función, mientras que las compilaciones de depuración no hacen eso (para propósitos de depuración).

Ahora me pregunto si es tan ansioso incluso que optimice la asignación de una variable no utilizada (lo que desencadena una operación en segundo plano).


Descargo de responsabilidad: Este es un detalle de implementación que está sujeto a cambios, tómelo con un grano de sal.

ECMA-335 de la especificación de la CLI, sección I.12.6.4 (Optimizaciones) establece lo siguiente:

Las implementaciones conformes de la CLI son libres de ejecutar programas utilizando cualquier tecnología que garantice, dentro de un solo hilo de ejecución, que los efectos secundarios y las excepciones generadas por un hilo sean visibles en el orden especificado por el CIL. Para este propósito, solo las operaciones volátiles (incluidas las lecturas volátiles) constituyen efectos secundarios visibles. (Tenga en cuenta que aunque solo las operaciones volátiles constituyen efectos secundarios visibles, las operaciones volátiles también afectan la visibilidad de las referencias no volátiles). Las operaciones volátiles se especifican en §I.12.6.7. No hay garantías de pedido relativas a las excepciones inyectadas en un subproceso por otro subproceso (tales excepciones a veces se denominan "excepciones asíncronas" (por ejemplo, System.Threading.ThreadAbortException).

[Fundamento: un compilador optimizador es libre de reordenar los efectos secundarios y las excepciones sincrónicas en la medida en que esta reordenación no cambie ningún comportamiento observable del programa. justificación final]

[Nota: una implementación de la CLI puede usar un compilador de optimización, por ejemplo, para convertir CIL a código de máquina nativo siempre que el compilador mantenga (dentro de cada subproceso de ejecución) el mismo orden de efectos secundarios y excepciones síncronas. Esta es una condición más fuerte que la ISO C ++ (que permite reordenar entre un par de puntos de secuencia) o el Esquema ISO (que permite reordenar los argumentos a funciones). nota final]

Esto significa que cualquier implementación conforme a la CLI es libre de realizar tales optimizaciones si puede garantizar que el orden de los efectos secundarios no se vea afectado. Esto significa que si un método no tiene efectos secundarios y el JIT o el compilador de lenguaje lo analiza de manera sistemática , puede optimizarlo, ya que no habrá un reordenamiento de dichos efectos secundarios con o sin ese método.

Dicho esto, actualmente, el compilador de C # optimizará la variable no utilizada, pero no la llamada al método. No hay un análisis estático de toda la llamada al método que realiza el compilador, por lo que no puede "probar" que el método no tiene ningún efecto secundario en su código. Más aún, las optimizaciones JIT no son tan agresivas, puede que solo incluyan la llamada al método, pero no la optimicen.

Al ser de código abierto, puede ver las fases de compilación del JIT x86 y ver algunas optimizaciones que se están realizando (a través de compphases.h ):

// Names of x86 JIT phases, in order. Assumes that the caller defines CompPhaseNameMacro // in a useful way before including this file, e.g., to define the phase enumeration and the // corresponding array of string names of those phases. This include file undefines CompPhaseNameMacro // after the last use. // The arguments are: // CompPhaseNameMacro(enumName, stringName, hasChildren, parent) // "enumName" is an Enumeration-style all-caps name. // "stringName" is a self-explanatory. // "hasChildren" is true if this phase is broken out into subphases. // (We should never do EndPhase on a phase that has children, only on ''leaf phases.'') // "parent" is -1 for leaf phases, otherwise it is the "enumName" of the parent phase. CompPhaseNameMacro(PHASE_PRE_IMPORT, "Pre-import", "PRE-IMP", false, -1) CompPhaseNameMacro(PHASE_IMPORTATION, "Importation", "IMPORT", false, -1) CompPhaseNameMacro(PHASE_POST_IMPORT, "Post-import", "POST-IMP", false, -1) CompPhaseNameMacro(PHASE_MORPH, "Morph", "MORPH", false, -1) CompPhaseNameMacro(PHASE_GS_COOKIE, "GS Cookie", "GS-COOK", false, -1) CompPhaseNameMacro(PHASE_COMPUTE_PREDS, "Compute preds", "PREDS", false, -1) CompPhaseNameMacro(PHASE_MARK_GC_POLL_BLOCKS, "Mark GC poll blocks", "GC-POLL", false, -1) CompPhaseNameMacro(PHASE_COMPUTE_EDGE_WEIGHTS, "Compute edge weights (1)", "EDG-WGT", false, -1) #if FEATURE_EH_FUNCLETS CompPhaseNameMacro(PHASE_CREATE_FUNCLETS, "Create EH funclets", "EH-FUNC", false, -1) #endif // FEATURE_EH_FUNCLETS CompPhaseNameMacro(PHASE_OPTIMIZE_LAYOUT, "Optimize layout", "LAYOUT", false, -1) CompPhaseNameMacro(PHASE_OPTIMIZE_LOOPS, "Optimize loops", "LOOP-OPT", false, -1) CompPhaseNameMacro(PHASE_CLONE_LOOPS, "Clone loops", "LP-CLONE", false, -1) CompPhaseNameMacro(PHASE_UNROLL_LOOPS, "Unroll loops", "UNROLL", false, -1) CompPhaseNameMacro(PHASE_HOIST_LOOP_CODE, "Hoist loop code", "LP-HOIST", false, -1) CompPhaseNameMacro(PHASE_MARK_LOCAL_VARS, "Mark local vars", "MARK-LCL", false, -1) CompPhaseNameMacro(PHASE_OPTIMIZE_BOOLS, "Optimize bools", "OPT-BOOL", false, -1) CompPhaseNameMacro(PHASE_FIND_OPER_ORDER, "Find oper order", "OPER-ORD", false, -1) CompPhaseNameMacro(PHASE_SET_BLOCK_ORDER, "Set block order", "BLK-ORD", false, -1) CompPhaseNameMacro(PHASE_BUILD_SSA, "Build SSA representation", "SSA", true, -1) CompPhaseNameMacro(PHASE_BUILD_SSA_TOPOSORT, "SSA: topological sort", "SSA-SORT", false, PHASE_BUILD_SSA) CompPhaseNameMacro(PHASE_BUILD_SSA_DOMS, "SSA: Doms1", "SSA-DOMS", false, PHASE_BUILD_SSA) CompPhaseNameMacro(PHASE_BUILD_SSA_LIVENESS, "SSA: liveness", "SSA-LIVE", false, PHASE_BUILD_SSA) CompPhaseNameMacro(PHASE_BUILD_SSA_IDF, "SSA: IDF", "SSA-IDF", false, PHASE_BUILD_SSA) CompPhaseNameMacro(PHASE_BUILD_SSA_INSERT_PHIS, "SSA: insert phis", "SSA-PHI", false, PHASE_BUILD_SSA) CompPhaseNameMacro(PHASE_BUILD_SSA_RENAME, "SSA: rename", "SSA-REN", false, PHASE_BUILD_SSA) CompPhaseNameMacro(PHASE_EARLY_PROP, "Early Value Propagation", "ERL-PROP", false, -1) CompPhaseNameMacro(PHASE_VALUE_NUMBER, "Do value numbering", "VAL-NUM", false, -1) CompPhaseNameMacro(PHASE_OPTIMIZE_INDEX_CHECKS, "Optimize index checks", "OPT-CHK", false, -1) #if FEATURE_VALNUM_CSE CompPhaseNameMacro(PHASE_OPTIMIZE_VALNUM_CSES, "Optimize Valnum CSEs", "OPT-CSE", false, -1) #endif CompPhaseNameMacro(PHASE_VN_COPY_PROP, "VN based copy prop", "CP-PROP", false, -1) #if ASSERTION_PROP CompPhaseNameMacro(PHASE_ASSERTION_PROP_MAIN, "Assertion prop", "AST-PROP", false, -1) #endif CompPhaseNameMacro(PHASE_UPDATE_FLOW_GRAPH, "Update flow graph", "UPD-FG", false, -1) CompPhaseNameMacro(PHASE_COMPUTE_EDGE_WEIGHTS2, "Compute edge weights (2)", "EDG-WGT2", false, -1) CompPhaseNameMacro(PHASE_DETERMINE_FIRST_COLD_BLOCK, "Determine first cold block", "COLD-BLK", false, -1) CompPhaseNameMacro(PHASE_RATIONALIZE, "Rationalize IR", "RAT", false, -1) CompPhaseNameMacro(PHASE_SIMPLE_LOWERING, "Do ''simple'' lowering", "SMP-LWR", false, -1) CompPhaseNameMacro(PHASE_LCLVARLIVENESS, "Local var liveness", "LIVENESS", true, -1) CompPhaseNameMacro(PHASE_LCLVARLIVENESS_INIT, "Local var liveness init", "LIV-INIT", false, PHASE_LCLVARLIVENESS) CompPhaseNameMacro(PHASE_LCLVARLIVENESS_PERBLOCK,"Per block local var liveness", "LIV-BLK", false, PHASE_LCLVARLIVENESS) CompPhaseNameMacro(PHASE_LCLVARLIVENESS_INTERBLOCK, "Global local var liveness", "LIV-GLBL", false, PHASE_LCLVARLIVENESS) CompPhaseNameMacro(PHASE_LVA_ADJUST_REF_COUNTS, "LVA adjust ref counts", "REF-CNT", false, -1) #ifdef LEGACY_BACKEND CompPhaseNameMacro(PHASE_RA_ASSIGN_VARS, "RA assign vars", "REGALLOC", false, -1) #endif // LEGACY_BACKEND CompPhaseNameMacro(PHASE_LOWERING_DECOMP, "Lowering decomposition", "LWR-DEC", false, -1) CompPhaseNameMacro(PHASE_LOWERING, "Lowering nodeinfo", "LWR-INFO", false, -1) #ifndef LEGACY_BACKEND CompPhaseNameMacro(PHASE_LINEAR_SCAN, "Linear scan register alloc", "LSRA", true, -1) CompPhaseNameMacro(PHASE_LINEAR_SCAN_BUILD, "LSRA build intervals", "LSRA-BLD", false, PHASE_LINEAR_SCAN) CompPhaseNameMacro(PHASE_LINEAR_SCAN_ALLOC, "LSRA allocate", "LSRA-ALL", false, PHASE_LINEAR_SCAN) CompPhaseNameMacro(PHASE_LINEAR_SCAN_RESOLVE, "LSRA resolve", "LSRA-RES", false, PHASE_LINEAR_SCAN) #endif // !LEGACY_BACKEND CompPhaseNameMacro(PHASE_GENERATE_CODE, "Generate code", "CODEGEN", false, -1) CompPhaseNameMacro(PHASE_EMIT_CODE, "Emit code", "EMIT", false, -1) CompPhaseNameMacro(PHASE_EMIT_GCEH, "Emit GC+EH tables", "EMT-GCEH", false, -1)

Algunas optimizaciones son:

  • Eliminación de código muerto
  • Asignación de registro de exploración lineal
  • Desenrollado de bucle
  • Eliminación de control de rango

Este artículo continúa describiendo algunas de las optimizaciones realizadas por el JIT, y hay una gran respuesta por parte de @EricLippert hablando en general sobre optimizaciones here


No, ni el compilador ni el JIT optimizarán su llamada de método.

Hay una lista de lo que hace el compilador JIT . Se optimiza lejos if (false) { ... } bloquea, por ejemplo, o asignaciones de variables no utilizadas. No solo optimiza tus llamadas de método. Si eso fuera cierto, todas las llamadas a un método void deberían desaparecer.


No.

Cualquier optimizador solo puede eliminar el código que no tenga un comportamiento observable.

De lo contrario no es un optimizador.