C# 中 ++i 和 i++ 之间有什么性能差异吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/467322/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 04:33:51  来源:igfitidea点击:

Is there any performance difference between ++i and i++ in C#?

c#operatorsperformance

提问by Anton

Is there any performance difference between using something like

使用类似的东西之间是否有任何性能差异

for(int i = 0; i < 10; i++) { ... }

and

for(int i = 0; i < 10; ++i) { ... }

or is the compiler able to optimize in such a way that they are equally fast in the case where they are functionally equivalent?

或者编译器是否能够以这样一种方式进行优化,使它们在功能相同的情况下同样快?

Edit: This was asked because I had a discussion with a co-worker about it, not because I think its a useful optimization in any practical sense. It is largely academic.

编辑:问这个是因为我与一位同事讨论过这个问题,而不是因为我认为它在任何实际意义上都是有用的优化。它主要是学术性的。

采纳答案by Jim Mischel

There is no difference in the generated intermediate code for ++i and i++ in this case. Given this program:

在这种情况下,++i 和 i++ 生成的中间代码没有区别。鉴于此程序:

class Program
{
    const int counter = 1024 * 1024;
    static void Main(string[] args)
    {
        for (int i = 0; i < counter; ++i)
        {
            Console.WriteLine(i);
        }

        for (int i = 0; i < counter; i++)
        {
            Console.WriteLine(i);
        }
    }
}

The generated IL code is the same for both loops:

两个循环生成的 IL 代码相同:

  IL_0000:  ldc.i4.0
  IL_0001:  stloc.0
  // Start of first loop
  IL_0002:  ldc.i4.0
  IL_0003:  stloc.0
  IL_0004:  br.s       IL_0010
  IL_0006:  ldloc.0
  IL_0007:  call       void [mscorlib]System.Console::WriteLine(int32)
  IL_000c:  ldloc.0
  IL_000d:  ldc.i4.1
  IL_000e:  add
  IL_000f:  stloc.0
  IL_0010:  ldloc.0
  IL_0011:  ldc.i4     0x100000
  IL_0016:  blt.s      IL_0006
  // Start of second loop
  IL_0018:  ldc.i4.0
  IL_0019:  stloc.0
  IL_001a:  br.s       IL_0026
  IL_001c:  ldloc.0
  IL_001d:  call       void [mscorlib]System.Console::WriteLine(int32)
  IL_0022:  ldloc.0
  IL_0023:  ldc.i4.1
  IL_0024:  add
  IL_0025:  stloc.0
  IL_0026:  ldloc.0
  IL_0027:  ldc.i4     0x100000
  IL_002c:  blt.s      IL_001c
  IL_002e:  ret

That said, it's possible (although highly unlikely) that the JIT compiler can do some optimizations in certain contexts that will favor one version over the other. If there is such an optimization, though, it would likely only affect the final (or perhaps the first) iteration of a loop.

也就是说,JIT 编译器有可能(尽管不太可能)在某些上下文中进行一些优化,这将有利于一个版本而不是另一个版本。但是,如果有这样的优化,它可能只会影响循环的最终(或可能是第一次)迭代。

In short, there will be no difference in the runtime of simple pre-increment or post-increment of the control variable in the looping construct that you've described.

简而言之,在您描述的循环结构中,控制变量的简单预增量或后增量的运行时间没有区别。

回答by M4N

According to this answer, i++ uses one CPU instruction more than ++i. But whether this results in a performance difference, I don't know.

根据this answer,i++比++i多使用一个CPU指令。但这是否会导致性能差异,我不知道。

Since either loop can easily be rewritten to use either a post-increment or a pre-increment, I guess that the compiler will always use the more efficient version.

由于任一循环都可以轻松重写为使用后增量或前增量,我猜编译器将始终使用更高效的版本。

回答by dan

Guys, guys, the "answers" are for C and C++.

伙计们,伙计们,“答案”是针对 C 和 C++ 的。

C# is a different animal.

C# 是另一种动物。

Use ILDASM to look at the compiled output to verify if there is an MSIL difference.

使用 ILDASM 查看编译输出以验证是否存在 MSIL 差异。

回答by Joe

Ah... Open again. OK. Here's the deal.

啊……再次打开。好的。这是交易。

ILDASM is a start, but not an end. The key is: What will the JIT generate for assembly code?

ILDASM 是一个开始,但不是结束。关键是:JIT 将为汇编代码生成什么?

Here's what you want to do.

这就是你想要做的。

Take a couple samples of what you are trying to look at. Obviously you can wall-clock time them if you want - but I assume you want to know more than that.

取一些您正在尝试查看的示例。显然,如果您愿意,您可以对它们进行挂钟计时 - 但我认为您想了解更多。

Here's what's not obvious. The C# compiler generates some MSIL sequences that are non-optimal in a lot of situations. The JIT it tuned to deal with these and quirks from other languages. The problem: Only 'quirks' someone has noticed have been tuned.

这是不明显的。C# 编译器会生成一些在很多情况下都不是最佳的 MSIL 序列。它经过调整以处理这些和其他语言的怪癖的 JIT。问题:只有有人注意到的“怪癖”才被调整。

You really want to make a sample that has your implementations to try, returns back up to main (or wherever), Sleep()s, or something where you can attach a debugger, then run the routines again.

您确实想要制作一个示例,让您可以尝试实现,返回到 main(或任何地方)、Sleep() 或可以附加调试器的地方,然后再次运行例程。

You DO NOT want to start the code under the debugger or the JIT will generate non-optimized code - and it sounds like you want to know how it will behave in a real environment. The JIT does this to maximize debug info and minimize the current source location from 'jumping around'. Never start a perf evaluation under the debugger.

您不想在调试器下启动代码,否则 JIT 将生成未优化的代码 - 听起来您想知道它在真实环境中的表现。JIT 这样做是为了最大限度地提高调试信息并最大限度地减少“跳跃”的当前源位置。切勿在调试器下启动性能评估。

OK. So once the code has run once (ie: The JIT has generated code for it), then attach the debugger during the sleep (or whatever). Then look at the x86/x64 that was generated for the two routines.

好的。因此,一旦代码运行一次(即:JIT 为其生成了代码),然后在睡眠期间(或其他)附加调试器。然后查看为两个例程生成的x86/x64。

My gut tells me that if you are using ++i/i++ as you described - ie: in a stand alone expression where the rvalue result is not re-used - there won't be a difference. But won't it be fun to go find out and see all the neat stuff! :)

我的直觉告诉我,如果您按照您的描述使用 ++i/i++ - 即:在不重复使用右值结果的独立表达式中 - 不会有区别。但是去寻找并查看所有整洁的东西不是很有趣吗!:)

回答by Michael Borgwardt

Have a concrete piece of code and CLR release in mind? If so, benchmark it. If not, forget about it. Micro-optimization, and all that... Besides, you can't even be sure different CLR release will produce the same result.

有具体的代码和 CLR 版本吗?如果是这样,请对其进行基准测试。如果没有,请忘记它。微优化等等……此外,您甚至无法确定不同的 CLR 版本会产生相同的结果。

回答by ggf31416

  static void Main(string[] args) {
     var sw = new Stopwatch(); sw.Start();
     for (int i = 0; i < 2000000000; ++i) { }
     //int i = 0;
     //while (i < 2000000000){++i;}
     Console.WriteLine(sw.ElapsedMilliseconds);

Average from 3 runs:
for with i++: 1307 for with ++i: 1314

3 次运行的平均值:
对于 i++:1307 对于 ++i:1314

while with i++ : 1261 while with ++i : 1276

使用 i++ 时:1261 使用 ++i 时:1276

That's a Celeron D at 2,53 Ghz. Each iteration took about 1.6 CPU cycles. That either means that the CPU was executing more than 1 instruction each cycle or that the JIT compiler unrolled the loops. The difference between i++ and ++i was only 0.01 CPU cycles per iteration, probably caused by the OS services in the background.

那是 2.53 Ghz 的赛扬 D。每次迭代大约需要 1.6 个 CPU 周期。这要么意味着 CPU 每个周期执行超过 1 条指令,要么 JIT 编译器展开循环。i++ 和 ++i 之间的差异仅为每次迭代 0.01 个 CPU 周期,可能是由后台的 OS 服务引起的。

回答by Jay Bazuzi

If you're asking this question, you're trying to solve the wrong problem.

如果你问这个问题,你是在试图解决错误的问题。

The first question to ask is "how to I improve customer satisfaction with my software by making it run faster?" and the answer is almost never "use ++i instead of i++" or vice versa.

要问的第一个问题是“我如何通过使其运行得更快来提高客户对我的软件的满意度?” 答案几乎从不“使用 ++i 而不是 i++”,反之亦然。

From Coding Horror's post "Hardware is Cheap, Programmers are Expensive":

来自 Coding Horror 的帖子“硬件很便宜,程序员很贵”:

Rules of Optimization:
Rule 1: Don't do it.
Rule 2 (for experts only): Don't do it yet.
-- M.A. Hymanson

优化
规则:规则 1:不要这样做。
规则 2(仅适用于专家):先不要这样做。
——马Hyman逊

I read rule 2 to mean "first write clean, clear code that meets your customer's needs, then speed it up where it's too slow". It's highly unlikely that ++ivs. i++is going to be the solution.

我认为规则 2 的意思是“首先编写满足客户需求的干净、清晰的代码,然后在太慢的地方加快速度”。++ivs.不太可能i++成为解决方案。

回答by Rasmus Faber

As Jim Mischel has shown, the compiler will generate identical MSIL for the two ways of writing the for-loop.

正如 Jim Mischel所示,编译器将为两种编写 for 循环的方式生成相同的 MSIL。

But that is it then: there is no reason to speculate about the JIT or perform speed-measurements. If the two lines of code generate identical MSIL, not only will they perform identically, they are effectively identical.

但就是这样:没有理由推测 JIT 或执行速度测量。如果这两行代码生成相同的 MSIL,它们不仅性能相同,而且实际上是相同的。

No possible JIT would be able to distinguish between the loops, so the generated machine code must necessarily be identical, too.

没有可能的 JIT 能够区分循环,因此生成的机器代码也必须相同。

回答by Hosam Aly

In addition to other answers, there can be a difference if your iis not an int. In C++, if it is an object of a class that has operators ++()and ++(int)overloaded, then it can make a difference, and possibly a side effect. Performance of ++ishouldbe better in this case (dependant on the implementation).

除了其他答案之外,如果您i不是int. 在 C++ 中,如果它是具有运算符++()++(int)重载的类的对象,那么它可能会有所作为,并且可能会产生副作用。在这种情况下,性能++i应该更好(取决于实现)。

回答by Raghu

There is no difference between both, The reason is in loop comparision is a separate statement after increment/decrement statement. Example ;

两者没有区别,原因是在循环比较是在增量/减量语句之后的单独语句。例子 ;

for(int i=0; i<3; i++){
    System.out.println(i)
}

This works like as below,

这如下所示,

Step initialize : i = 0

步骤初始化: i = 0

Step Compare : i < 3,it is true then execute loopblock

步骤比较:i < 3,为真则执行loop

Step Increment : i = i++;or i = ++i;

步长:i = i++;i = ++i;

Step Copmare Again : As Java is doing comparision in next step. Both i++ and ++i gives same result. For example if i = 0, after increment i will become 1.

Step Copmare Again : 因为 Java 在下一步做比较。i++ 和 ++i 都给出相同的结果。例如,如果 i = 0,则递增后 i 将变为 1。

Thats why either pre increment or post increment behaves same in loop.

这就是为什么前增量或后增量在循环中的行为相同的原因。