在函数内部给出以下C#代码:
....
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;
注意:我不返回documentCollection
,我只需要初始化它,如果还没有(CreateDocumentCollectionAsync
调用)。所以 - 在if
块后,documentCollection
成为未使用的变量。
现在 - ReSharper建议将其优化为:
var documentCollection =
client.CreateDocumentCollectionQuery("dbs/" + database.Id)
.Where(c => c.Id == DocumentCollectionName)
.AsEnumerable()
.FirstOrDefault()
?? await
client.CreateDocumentCollectionAsync(
"dbs/" + database.Id,
new DocumentCollection { Id = DocumentCollectionName });
现在表明documentCollection
是一个未使用的变量。
我的问题:C#代码优化还是'发布'版本会完全删除这行代码并导致CreateDocumentCollectionAsync
永远不会触发?
C#优化课程告诉我,'release'会在函数中不需要“下线”时立即构建垃圾收集变量,而调试版本则不会这样做(出于调试目的)。
我现在想知道它是否如此渴望,即使它优化了一个未使用的变量赋值(它在后台触发操作)。
答案 0 :(得分:25)
不,编译器和JIT都不会优化你的方法调用。
有一个list of what the JIT compiler does。例如,它会优化远离if (false) { ... }
块或未使用的变量赋值。它不仅可以优化您的方法调用。如果这是真的,那么对void
方法的每次调用都应该消失。
答案 1 :(得分:16)
否强>
任何优化器都只能删除没有可观察行为的代码。
否则它不是一个优化者。
答案 2 :(得分:14)
声明: 这是实施细节,可能会发生变化,并带有一丝盐味。
CLI规范的ECMA-335,第I.12.6.4节(优化)声明如下:
符合CLI的实现可以自由执行程序 在单个线程中使用任何保证的技术 执行,线程产生的副作用和异常 以CIL指定的顺序可见。仅为此目的 易失性操作(包括易失性读取)构成可见 副作用。 (注意,虽然只有易失性操作构成 可见的副作用,挥发性操作也会影响能见度 非易失性引用。)挥发性操作在 §I.12.6.7。相对于异常没有排序保证 由另一个线程注入一个线程(例如 有时称为“异步异常”(例如, System.Threading.ThreadAbortException)。
[理由:优化 编译器可以重新排序副作用和同步异常 此重新排序的程度不会改变任何可观察的程序 行为。最终理由]
[注意:一个 CLI的实现允许使用优化编译器, 例如,将CIL转换为提供的本机机器代码 编译器维护(在每个执行的线程内)相同 副作用和同步异常的顺序。这是一个更强大的 条件比ISO C ++(允许在一对之间重新排序) 序列点)或ISO方案(允许重新排序参数 功能)。结束说明]
这意味着任何符合CLI的实现都可以自由地进行这样的优化,如果它可以保证副作用的顺序不会受到损害。这意味着如果方法没有任何一方-effect和JIT或语言编译器静态分析对于给定事实,它可以优化它,因为不会重新排序所述副作用或没有那种方法。
话虽如此,目前,C#编译器将优化掉未使用的变量,但不会优化方法调用。编译器没有对整个方法调用进行静态分析,因此无法“证明”该方法在代码中没有副作用。更重要的是,JIT优化不是那么激进,它可能只是内联方法调用,而不是优化它。
作为开源,您可以看到x86 JIT编译阶段和get a look at some optimizations being done(通过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)
一些优化是:
This article接着描述了JIT所做的一些优化,@ EricLippert总体上讨论了优化问题here