C#编译器和优化会破坏这段代码吗?

时间:2016-01-06 09:41:04

标签: c# optimization compilation resharper

在函数内部给出以下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'会在函数中不需要“下线”时立即构建垃圾收集变量,而调试版本则不会这样做(出于调试目的)。

我现在想知道它是否如此渴望,即使它优化了一个未使用的变量赋值(它在后台触发操作)。

3 个答案:

答案 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