【问题标题】:Is there any performance difference with ++i vs i += 1 in C#?C# 中的 ++i 与 i += 1 有什么性能差异吗?
【发布时间】:2011-10-16 21:46:48
【问题描述】:

i += a 应该等价于 i = i + a。 在 a == 1 的情况下,这应该不如 ++i 效率低,因为它涉及对内存的更多访问;还是编译器会让它和 ++i 完全一样?

【问题讨论】:

  • @paxdiablo:太棒了。从现在开始,我将帮助宣传您的这些帖子。
  • 避免过早的优化,你不应该担心这个。您的编译器是一段非常智能的代码,学会在这类事情上信任它并专注于您想要解决的问题。
  • 谢谢。我看过与 ++i 和 i++ 相关的问题,但特别想知道与 += 1 的区别(因为该运算符可用于与其他值一起递增)。
  • @Orhan,在这种情况下,询问而不是分析的目的是寻求由一些技术洞察力支持的答案,并且在良好的编程习惯方面尽可能通用。

标签: c# performance increment


【解决方案1】:

很容易回答:C# 编译器将 C# 源代码转换为 IL 操作码。没有专门的 IL 操作码可以执行与 ++ 运算符等效的操作。如果您使用 ildasm.exe 工具查看生成的 IL,这很容易看出。此示例 C# sn-p:

        int ix = 0;
        ix++;
        ix = ix + 1;

生成:

  IL_0000:  ldc.i4.0               // load 0
  IL_0001:  stloc.0                // ix = 0

  IL_0002:  ldloc.0                // load ix
  IL_0003:  ldc.i4.1               // load 1
  IL_0004:  add                    // ix + 1
  IL_0005:  stloc.0                // ix = ix + 1

  IL_0006:  ldloc.0                // load ix
  IL_0007:  ldc.i4.1               // load 1
  IL_0008:  add                    // ix + 1
  IL_0009:  stloc.0                // ix = ix + 1

它生成完全相同的代码。除了生成同样快的机器代码外,抖动无能为力。

前/后自增运算符是 C# 中的语法糖,在任何可以使代码更清晰的地方使用它。或者可能更相关:在它不太易读的地方避免它。他们确实有让您创建具有太多副作用的表达式的诀窍。

【讨论】:

  • 我没有理解任何 IL 代码行。这种语言叫什么名字?我想在 Google 中搜索它。
  • 它是 IL,中间语言。使用“msil”更容易在 Google 上搜索。
【解决方案2】:

编译器应该优化你编写的代码,所以我相信 i = i + 1 与 ++i 相同。

【讨论】:

  • 编译器将处理i++i = i + 1 不同,但我猜你的意思是++i
【解决方案3】:

我认为不会有任何区别,但是您可以将其付诸实践,例如:

class Program
{
    static void Main(string[] args)
    {
        //Add values
        List<objClass> lst1 = new List<objClass>();
        for (int i = 0; i < 9000000; i++)
        {
            lst1.Add(new objClass("1", ""));
        }

        //For loop ++i
        DateTime startTime = DateTime.Now;
        for (int i = 0; i < 9000000; ++i)
        {
            lst1[i]._s1 = lst1[i]._s2;
        }
        Console.WriteLine((DateTime.Now - startTime).ToString());

        //For loop i+=1
        startTime = DateTime.Now;
        for (int i = 0; i < 9000000; i+=1)
        {
            lst1[i]._s1 = lst1[i]._s2;
        }
        Console.WriteLine((DateTime.Now - startTime).ToString());

    }
public class objClass
    {
        public string _s1 { get; set; }
        public string _s2 { get; set; }

        public objClass(string _s1, string _s2)
        {
            this._s1 = _s1;
            this._s2 = _s2;
        }
    }

}

【讨论】:

  • 使用 StopWatch 实例会更合适,但谢谢。
  • 00:00:00.2510144 与 00:00:00.3160181
【解决方案4】:

这取决于。理论上,给定一个足够幼稚的编译器,++x 可能比x += 1 更有效。

但是,我不知道有这么幼稚的编译器。

  • 如果操作数的值在编译时已知,则编译器可以优化操作。
  • 如果可以确定该值在运行时保持不变,则 JIT 编译器也可以优化操作。

此外,现代 CPU 是高度复杂的野兽,它们可以并行执行大量操作,而且很多时候,通过将这些“简单”操作与更大、更复杂的操作并行运行,可以完全隐藏这些操作。

一个好的经验法则是,在优化和一般写作时尽可能清楚地表达你的意图

这使其他程序员更容易阅读您的代码,但它使编译器更容易阅读。

如果您想增加一个值,请使用自增运算符 (++),因为它准确地描述了您想要做什么。

如果要添加变量或未知值,请使用+= 运算符,因为这就是它的用途

如果您清楚自己的意图,那么编译器会获取有关您的代码的更多信息,并可以进行相应的优化。

如果你写x += 1,你真的是在试图让编译器出错。您说的是“使用通用加法运算符将一加到x”。然后编译器必须弄清楚“好吧,你说过要使用通用加法,但我可以看到这不是必需的,所以我只需要递增”。你本可以这么说的。现在,您必须依靠聪明的编译器。

【讨论】:

    【解决方案5】:

    除非 a 定义为 const int == 1,否则编译器如何在编译时知道 a == 1?

    所以答案一定是否定的,编译器不能把这个编译成++i。

    【讨论】:

    • 编译器可以知道,因为 a: 它被调用为 i += 1,即在 rhs 上带有文字 1,在这种情况下编译器可以简单地内联它,或者 b: 因为 C# 是JIT 编译,所以如果在运行时,JIT 可以确定参数为 1,它可以相应地优化操作。所以答案“必须”肯定不是。可能是,但不是必须
    • a 是一个变量,JITter 在编译时或 JIT 时无法知道它的值,除非它是一个常量。也许我们以不同的方式阅读这个问题。我现在在想问题的标题和正文实际上并不是在问同样的事情。
    • 如果 a 是一个变量,那么编译器确实无法事先优化代码。当 a 是文字(即写为“1”)时,我的问题更多。
    • 吹毛求疵:一个(跟踪)JIT 编译器实际上可以根据在运行时发现的一些值进行优化,并添加一个防护以在值发生变化时中断。他们只是选择不使用正常变量,因为它们是可变的,因此可能会很快使跟踪无效。在实践中,或者即使它确实发生了,这对于这个问题并不重要。
    • @James:它当然可以知道 JIT 时间的值。它也许可以推断出即使变量没有声明const,它在此处始终具有值1,因此可以对其进行优化。当然,如果你不知道操作数是 1,那么比较 +++= 是没有意义的
    猜你喜欢
    • 1970-01-01
    • 2010-09-13
    • 2010-10-02
    • 1970-01-01
    • 2011-09-22
    • 1970-01-01
    • 2018-05-21
    • 2015-08-17
    相关资源
    最近更新 更多