【问题标题】:Will the C# compiler optimize variable away?C# 编译器会优化变量吗?
【发布时间】:2019-01-01 01:39:30
【问题描述】:

这是我的帖子Is this a correct implementation of a concurrent observable collection? 的后续。

在那篇文章中,我有一个实现通用并发可观察列表的自定义类,包括IEnumerable<T>.GetEnumerator() 的实现。这是原始代码:

public IEnumerator<T> GetEnumerator()
{
    var localSnapshot = _snapshot; //create local variable to protect enumerator, if class member (_snapshot) should be changed/replaced while iterating
    return ((IEnumerable<T>)localSnapshot).GetEnumerator();
}

_snapshot 是一个私有的 Array 字段,只要实际内部集合 (List&lt;T&gt; _list) 被修改,就会使用 lock() 重新构建。

但现在我觉得localSnapshot这个变量根本不需要,代码应该是:

public IEnumerator<T> GetEnumerator()
{
    return ((IEnumerable<T>)_snapshot).GetEnumerator();
}

因为localSnapshot 被简单地分配了对_snapshot 引用的同一地址的引用。 GetEnumerator 不关心(也无法分辨)使用了哪个变量(当然,它会为自己创建另一个引用同一数组的变量)。

如果我的上述假设是正确的,我想知道编译器是否会将变量优化掉?然后生成的代码将是相同的。或者,如果不是:理论上复制引用可能是“有害的”,因为副本的更新程度将低于可能的情况(另一个线程可以在复制完成后刷新_snapshot,但在GetEnumerator 之前叫做)?而且,编译器不优化代码的原因是“副作用”吗?因为优化是“无副作用”的?

【问题讨论】:

  • 要可靠地回答这个问题,您需要自己测试一下,语言编译器,或者很可能 Jitter 可以很好地优化它。如果您发现自己在问这些问题,检查发布模式下的IL,也可以使用sharplab.io或其他工具检查asm
  • 在这种情况下,优化将由编译器完成。相同代码的两个版本将产生相同的 IL 这两个代码 sn-ps 在功能上没有区别

标签: c# multithreading compiler-optimization


【解决方案1】:

两个版本的代码在编译后会产生相同的结果。正如 TheGeneral 在 cmets 中指出的那样,确保检查的一个好方法是查看sharplab.io

请注意,只有在发布模式下编译时才会如此。如果你在 Debug 模式下编译,那么编译器会假设你可能需要中间变量来进行调试并且不会对其进行优化。

理论上复制引用甚至可能是“有害的”,因为副本的更新程度可能低于实际情况(另一个线程可以在复制完成后但在调用 GetEnumerator 之前刷新 _snapshot)

如果您在var localSnapshot = _snapshot;return ((IEnumerable&lt;T&gt;)localSnapshot).GetEnumerator() 之间执行了一些代码,就会出现这种情况。在那种情况下,优化是不可能的。否则,在这两种情况下,您都在读取值并直接使用它。两个版本的代码没有“新鲜度”的区别。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多