【问题标题】:Can the compiler optimize away ToString() on a string?编译器可以优化字符串上的 ToString() 吗?
【发布时间】:2014-08-08 18:07:55
【问题描述】:

我相信每个人都遇到过他们喜欢ToString() 方法的开发人员。我们都可能见过类似以下的代码:

public static bool CompareIfAnyMatchesOrHasEmpty(List<string> list1, List<string> list2)
{
    bool result = false;
    foreach (string item1 in list1)
    {
        foreach (string item2 in list2)
        {
            if (item1.ToString() == item2.ToString())
            {
                result = true;
            }
            if (item1.ToString() == "")
            {
                result = true;
            }
        }
    }
    return result;
}

我想知道的是 ToString() 方法(空的,没有格式化的)是否可以被编译器优化掉?我的假设是它没有,因为它最初是在object 上定义的。因此,我提出第二个问题,是否值得努力清理此类实例?

【问题讨论】:

  • 那段代码简直让我脊背发凉!
  • be optimization away 几乎没有相关性,它的操作太简单了。这是代码质量问题,而不是性能问题。
  • 最好优化掉写这篇文章的开发者。
  • 顺便问一下,这是真实代码吗?除了 ToString 之外,我还有其他问题。就像运行时是 O(M*N) 的事实一样,因为没有中断或返回语句......
  • 请注意:以上是人为的代码,接近(但不完全)我见过的最糟糕的代码。这个问题的灵感来自this TheDailyWTF,它带回了太多不好的回忆。

标签: c# compiler-optimization


【解决方案1】:

C# 编译器不会对此进行优化。但是,在运行时,我相信这可能会被 CLR 中的 JIT 编译器内联,因为 string.ToString() 只是返回自身。

String.ToString 甚至被声明为TargetedPatchingOptOutAttribute,这使得它在从其他程序集调用时也可以被 NGEN 内联,因此它显然是一个内联目标。

【讨论】:

  • 然后被a编译器优化掉。
  • @HenkHolterman 是的 - C# 编译器不会,但 JIT 会。
  • 我对@9​​87654324@ 感兴趣。有人可以在此答案中包含“内联原生图像生成器 (NGen) 图像”的“虚拟指南”吗?
  • @MikeGuthrie 如果您使用 NGEN,它不会(通常)内联跨越程序集边界的调用 - 否则,更新的 mscorlib 会改变行为。这个属性允许无论如何内联。
  • 在这里接受答案真的很痛苦,所以想发表评论。感谢您的回答 - 它确实回答了问题的标题,我学到了一些新东西。但是,我选择了阿奎那的回答,因为我认为它解决了真正问题的核心,即在他的最后一段中。
【解决方案2】:

它当然可以被编译器优化掉,但他们可能因为它是微不足道的。在决定任何优化是否值得之前,请先尝试一些测试。让我们试试吧!

List<string> strings = Enumerable.Range(1, 10000000).Select(x => Guid.NewGuid().ToString()).ToList();
var sw= Stopwatch.StartNew();

foreach (var str in strings) {
    if (!str.ToString().Equals(str.ToString())) {
        throw new ApplicationException("The world is ending");
    }
}

sw.Stop();
Console.WriteLine("Took: " + sw.Elapsed.TotalMilliseconds);

sw = Stopwatch.StartNew();
foreach (var str in strings) {
    if (!str.Equals(str)) {
        throw new ApplicationException("The world is ending");
    }
}
sw.Stop();
Console.WriteLine("Took: " + sw.Elapsed.TotalMilliseconds);

好的,所以我们处于包含 1000 万个项目的循环中。与非 tostring 版本相比,tostring(调用两次)版本需要多长时间?

这是我在我的机器上得到的:

Took: 261.6189 
Took: 231.2615

所以,是的。我在 1000 万次迭代中节省了整整 30 毫秒。所以......是的,我要说不,不值得。完全没有。

现在,是否应该更改代码,因为它是愚蠢?是的。我会提出这样的论点,“这是不必要的,让我一眼就认为这不是一个字符串。它需要我的大脑周期来处理,并且实际上没有任何目的。不要这样做。”不要从优化的角度争论。

【讨论】:

  • 始终说明您是如何运行基准测试的。发布版本,在 VS 之外?这个也应该首先与另一个循环一起运行。
  • 为什么要先运行另一个循环?在任何情况下:发布版本,在 VS 之外:Took:229.8982 Take:187.611 我在切换循环的情况下运行它,结果几乎相同。我很好奇你为什么认为这很重要?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-07
  • 2016-07-21
  • 2017-11-05
  • 1970-01-01
  • 1970-01-01
  • 2011-05-16
相关资源
最近更新 更多