当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《C#编译器和优化会破坏这段代码吗?》经验,为你挑选了3个好方法。

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

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



1> Patrick Hofm..:

不,编译器和JIT都不会优化您的方法调用.

有一个JIT编译器的列表.它确实优化了if (false) { ... }例如块或未使用的变量赋值.它不仅可以优化您的方法调用.如果这是真的,那么对void方法的每次调用都应该消失.


那个列表(假设我找到了正确的列表)既不明确,也不完整,肯定会有变化,因为优化只不过是实现细节.编译器必须运行的规则可以简化(忽略并发),因为"任何优化都是有效的,只要它不会改变可观察的程序行为".因此,如果编译器可以证明调用没有可观察到的影响,那么是的,它可以优化它们.

2> Lightness Ra..:

没有.

任何优化器都只能删除没有可观察行为的代码.

否则它不是一个优化者.



3> Yuval Itzcha..:

免责声明:这是实施细节,可能会有所变化,并带有一丝盐.


CLI规范的ECMA-335,第I.12.6.4节(优化)声明如下:

符合CLI的实现可以使用任何技术自由执行程序,这些技术在单个执行线程中保证线程生成的副作用和异常按照CIL指定的顺序可见.为此目的,仅挥发性操作(包括挥发性读取)构成可见的副作用.(请注意,虽然只有易失性操作构成可见的副作用,但易失性操作也会影响非易失性引用的可见性.)易失性操作在§I.12.6.7中指定.相对于另一个线程注入线程的异常,没有排序保证(此类异常有时称为"异步异常"(例如,System.Threading.ThreadAbortException).

[基本原理:优化编译器可以自由地重新排序副作用和同步异常,只要这种重新排序不会改变任何可观察的程序行为.最终理由]

[注意:允许CLI的实现使用优化编译器,例如,将CIL转换为本机机器代码,前提是编译器维护(在每个执行线程内)相同的副作用和同步异常顺序.这是比ISO C++(允许在一对序列点之间重新排序)或ISO Scheme(允许对函数的参数重新排序)更强的条件.结束说明]

这意味着符合CLI的任何实现都可以自由地进行这样的优化,如果它可以保证副作用的顺序不会受到损害.这意味着如果方法没有副作用并且JIT或语言编译器静态地分析对于给定事实,则它可以优化它,因为在使用或不使用该方法的情况下将不会重新排序所述副作用.

话虽如此,目前,C#编译器优化掉未使用的变量,但不会优化方法调用.编译器没有对整个方法调用进行静态分析,因此无法"证明"该方法在代码中没有副作用.更重要的是,JIT优化并不具有攻击性,它可能只是内联方法调用,而不是优化它.

作为开源,您可以看到x86 JIT编译阶段并查看正在进行的一些优化(via 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)

一些优化是:

死代码消除

线性扫描寄存器分配

循环展开

范围检查消除

本文接着描述了JIT所做的一些优化,@ EricLippert总体上讨论了优化问题


@IllidanS4如果方法被标记为`[Pure]`并且返回值被丢弃,则**可能会被删除死码.更多关于[这里](http://lwn.net/Articles/285332/)
推荐阅读
农大军乐团_697
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有