编译器能够优化掉字符串的ToString()方法吗?

3

我相信每个人都遇到过喜欢使用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上。 因此,我提出了第二个问题,是否值得清理这样的实例?

10
那段代码让我毛骨悚然! - Nathan A
3
"be optimized away" 几乎不相关,它是一个过于简单的操作。这是代码质量问题,而非性能问题。 - H H
5
你最好将编写这段代码的开发人员优化掉。 - Michael McGriff
顺便问一句,这是真实的代码吗?除了ToString之外,我还有其他问题。比如运行时是O(M*N),因为没有break或return语句... - aquinas
我曾经看到过很多"string constant".ToString()的用法,甚至将Decimal格式化为货币字符串,然后尝试将其解析为int,再次调用ToString(),并且被诚实地问到为什么会引发异常。此外,我认为在每个字符串上随意使用.ToString()将“允许”您获得更多的NullReferenceException... - Mike Guthrie
显示剩余2条评论
2个回答

8

C#编译器不会对此进行优化。然而,在运行时,我相信JIT编译器在CLR中可能会将其内联,因为string.ToString()只是返回它本身。

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


然后它被编译器优化掉了。 - H H
1
@HenkHolterman 是的 - C#编译器不会,但JIT会。 - Reed Copsey
我对 TargetedPatchingOptOutAttribute 感兴趣。能否有人在答案中包含一个关于“在本机映像生成器(NGen)映像中内联”的“白痴指南”? - Mike Guthrie
如果使用NGEN,它通常不会内联跨程序集边界的调用 - 否则,更新后的mscorlib将更改行为。该属性允许仍然内联。 - Reed Copsey
真的很纠结是否接受这里的答案,所以想要评论一下。感谢你的回答 - 它确实回答了问题的标题,并且我学到了新东西。然而,我选择了aquinas的答案,因为我认为他在最后一段中解决了真正问题的核心。 - Mike Guthrie

7

这段代码可能会被编译器优化掉,但它很简单,所以编译器可能不会这样做。在决定是否值得进行任何优化之前,请先进行一些测试。让我们试试吧!

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);

好的,我们正在处理一百万个项目的循环。相比于非tostring版本,调用两次tostring所需的时间有多长?

这是我在我的电脑上得到的结果:

Took: 261.6189 
Took: 231.2615

嗯,是的。在1000万次迭代中,我节省了整整30毫秒。所以...是的,我会说不值得。根本不值得。

现在,是否应该更改代码,因为它很“愚蠢”?是的。我会这样说,“这是不必要的,并且让我一眼看去认为这不是一个字符串。这需要我的大脑处理,而且完全没有意义。不要这样做。”不要从优化的角度来争论。


1
始终说明您如何运行基准测试。发布版本,是否在VS之外?这个也应该先运行另一个循环。 - H H
1
为什么另一个循环应该先运行?无论如何: Release版本,在VS之外: Took: 229.8982 Took: 187.611 我将循环交换后再次运行,结果几乎相同。我想知道你为什么认为它很重要? - aquinas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接