32

我的印象是这两个命令导致相同的结果,即将 X 增加 1,但后者可能更有效。

如果这不正确,请解释差异。

如果是正确的,为什么后者应该更有效率?他们不应该都编译成同一个IL吗?

谢谢。

4

17 回答 17

109

来自+= 的 MSDN 库

使用此运算符与指定 result = result + expression 几乎相同,只是 result 只计算一次。

所以它们并不相同,这就是为什么 x += 1 会更有效。

更新:我刚刚注意到我的 MSDN Library 链接指向 JScript 页面而不是VB 页面,它不包含相同的引用。

因此,经过进一步的研究和测试,该答案不适用于 VB.NET。我错了。这是一个示例控制台应用程序:

Module Module1

Sub Main()
    Dim x = 0
    Console.WriteLine(PlusEqual1(x))
    Console.WriteLine(Add1(x))
    Console.WriteLine(PlusEqual2(x))
    Console.WriteLine(Add2(x))
    Console.ReadLine()
End Sub

Public Function PlusEqual1(ByVal x As Integer) As Integer
    x += 1
    Return x
End Function

Public Function Add1(ByVal x As Integer) As Integer
    x = x + 1
    Return x
End Function

Public Function PlusEqual2(ByVal x As Integer) As Integer
    x += 2
    Return x
End Function

Public Function Add2(ByVal x As Integer) As Integer
    x = x + 2
    Return x
End Function

End Module

PlusEqual1 和 Add1 的 IL 确实是相同的:

.method public static int32 Add1(int32 x) cil managed
{
.maxstack 2
.locals init (
    [0] int32 Add1)
L_0000: nop 
L_0001: ldarg.0 
L_0002: ldc.i4.1 
L_0003: add.ovf 
L_0004: starg.s x
L_0006: ldarg.0 
L_0007: stloc.0 
L_0008: br.s L_000a
L_000a: ldloc.0 
L_000b: ret 
}

PlusEqual2 和 Add2 的 IL 也几乎相同:

.method public static int32 Add2(int32 x) cil managed
{ 
.maxstack 2
.locals init (
    [0] int32 Add2)
L_0000: nop 
L_0001: ldarg.0 
L_0002: ldc.i4.2 
L_0003: add.ovf 
L_0004: starg.s x
L_0006: ldarg.0 
L_0007: stloc.0 
L_0008: br.s L_000a
L_000a: ldloc.0 
L_000b: ret 
}
于 2009-04-30T17:25:52.400 回答
26

我写了一个简单的控制台应用程序:

static void Main(string[] args)
{
    int i = 0;
    i += 1;
    i = i + 1;
    Console.WriteLine(i);
}

我使用 Reflector 对其进行了拆卸,这就是我得到的:

private static void Main(string[] args)
{
    int i = 0;
    i++;
    i++;
    Console.WriteLine(i);
}

他们是一样的。

于 2009-04-30T17:29:29.990 回答
18

它们编译成相同的,第二个更容易输入。

于 2009-04-30T17:17:58.613 回答
11

重要的:

+=在一般语言中,指定评估的答案在做什么方面肯定是正确的。但是在 VB.NET 中,我假设X在 OP 中指定的是一个变量或一个属性。


它们可能会编译为相同的 IL。

更新(以解决可能存在的争议):

VB.NET 是一种编程语言规范。任何符合规范中定义的编译器都可以是 VB.NET 实现。如果您编辑 MS VB.NET 编译器的源代码以生成用于X += 1案例的蹩脚代码,您仍然会符合 VB.NET 规范(因为它没有说明它将如何工作。它只是说明了效果将完全相同,这使得生成相同的代码是合乎逻辑的,确实)。

虽然编译器很可能(我觉得确实如此)为两者生成相同的代码,但它是一个相当复杂的软件。哎呀,当相同的代码被编译两次时,你甚至不能保证编译器生成完全相同的代码!

您可以 100% 确定地说(除非您非常了解编译器的源代码)是一个好的编译器应该生成相同的代码,性能方面,这可能是也可能不是完全相同的代码

于 2009-04-30T17:17:32.467 回答
8

这么多猜测!即使是反射器的结论也不一定正确,因为它可以在拆卸时进行优化。

那么为什么你们没有人只看一下 IL 代码呢?看看下面的 C# 程序:

static void Main(string[] args)
{
    int x = 2;
    int y = 3;
    x += 1;
    y = y + 1;
    Console.WriteLine(x);
    Console.WriteLine(y);
}

此代码段编译为:

.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 25 (0x19)
.maxstack 2
.locals init ([0] int32 x,
[1] int32 y)
// some commands omitted here

IL_0004: ldloc.0
IL_0005: ldc.i4.1
IL_0006: add
IL_0007: stloc.0

IL_0008: ldloc.1
IL_0009: ldc.i4.1
IL_000a: add
IL_000b: stloc.1

// some commands omitted here
}

正如你所看到的,它实际上是完全一样的。为什么会这样?因为 IL 的目的是告诉我们该做什么,而不是如何去做。优化将是 JIT 编译器的工作。顺便说一句,在 VB.Net 中是一样的

于 2009-04-30T18:03:13.323 回答
4

在 x86 上,如果 x 在寄存器 eax 中,它们都会导致类似

公司;

所以你是对的,经过一些编译阶段,IL 将是相同的。

有一整类这样的问题可以用“相信你的优化器”来回答。

著名的神话是
x++;
效率低于
++x;
因为它必须存储一个临时值。如果您从不使用临时值,优化器将删除该存储。

于 2009-04-30T17:24:25.837 回答
2

它们在 VB 中可能相同;它们在 C 中不一定相同(运算符来自哪里)。

于 2009-04-30T17:18:27.333 回答
2
  1. 是的,它们的行为相同。
  2. 不,它们可能同样有效。优化器擅长这类事情。如果您想仔细检查,请编写优化代码并在反射器中查看。
于 2009-04-30T17:18:40.420 回答
2

如果 x 是像 int 或 float 这样的简单类型,优化器可能会产生相同的结果。

如果您使用其他语言(此处的 VB 知识有限,您可以重载 += 吗?)其中 x 可能是一个大喇叭对象,前者会创建和额外的副本,可能是数百兆。后者没有。

于 2009-04-30T17:19:08.330 回答
2

是相同的。

x=x+1 

数学被认为是矛盾的,而

x+=1

不是而且很容易打字。

于 2009-04-30T17:20:51.197 回答
1

在 C++ 中,它取决于x是什么数据类型以及如何定义运算符。如果x是某个类的实例,您可以获得完全不同的结果。

或者也许你应该解决这个问题并指定x是一个整数或其他什么。

于 2009-04-30T18:56:18.767 回答
1

我认为差异是由于用于内存引用的额外时钟周期,但结果我错了!我自己无法理解这件事

instruction type        example                      cycles

==================================================== ==================

ADD reg,reg             add ax,bx                       1
ADD mem,reg             add total, cx                   3
ADD reg,mem             add cx,incr                     2
ADD reg,immed           add bx,6                        1
ADD mem,immed           add pointers[bx][si],6          3
ADD accum,immed         add ax,10                       1

INC reg                 inc bx                          1
INC mem                 inc vpage                       3

MOV reg,reg             mov bp,sp                       1
MOV mem,reg             mov array[di],bx                1
MOV reg,mem             mov bx,pointer                  1
MOV mem,immed           mov [bx],15                     1
MOV reg,immed           mov cx,256                      1
MOV mem,accum           mov total,ax                    1
MOV accum,mem           mov al,string                   1
MOV segreg,reg16        mov ds,ax                       2, 3
MOV segreg,mem16        mov es,psp                      2, 3
MOV reg16,segreg        mov ax,ds                       1
MOV mem16,segreg        mov stack_save,ss               1
MOV reg32,controlreg    mov eax,cr0                     22
                        mov eax,cr2                     12
                        mov eax,cr3                     21, 46
                        mov eax,cr4                     14
MOV controlreg,reg32    mov cr0,eax                     4
MOV reg32,debugreg      mov edx,dr0                     DR0-DR3,DR6,DR7=11;
                                                        DR4,DR5=12 
MOV debugreg,reg32      mov dr0,ecx                     DR0-DR3,DR6,DR7=11;
                                                        DR4,DR5=12 

来源:http ://turkish_rational.tripod.com/trdos/pentium.txt

指令可以翻译为:

;for i = i+1   ; cycles
mov  ax,   [i]  ; 1
add  ax,   1    ; 1
mov  [i],  ax   ; 1

;for i += 1
; dunno the syntax of instruction. it should be the pointers one :S     

;for i++
inc  i          ; 3
;or
mov  ax,   [i]  ; 1
inc  ax         ; 1
mov  [i],  ax   ; 1

;for ++i
mov  ax,   [i]  ; 1
;do  stuff      ; matters not
inc  ax         ; 1
mov  [i],  ax   ; 1

结果都是一样的:S它只是一些可能有用的数据。请给出意见!

于 2009-04-30T18:57:00.247 回答
1

值得注意的是 +=、-=、*= 等进行了隐式转换。

int i = 0;
i = i + 5.5; // doesn't compile.
i += 5.5; // compiles.
于 2009-05-01T06:38:34.897 回答
0

在运行时(至少使用 PERL)没有区别。x+=1 比 x = x+1 快大约 0.5 秒

于 2009-04-30T17:18:18.893 回答
0

程序效率没有区别;只是打字效率。

于 2009-04-30T17:20:24.660 回答
0

早在 1980 年代初期,Lattice C 编译器的一项非常酷的优化就是“x = x + 1;”、“x += 1;” 和“x++;” 都产生了完全相同的机器代码。如果他们可以做到,那么这个千年编写的编译器肯定可以做到。

于 2009-04-30T17:24:43.853 回答
0

如果 x 是一个简单的整数标量变量,它们应该是相同的。

如果 x 是一个大表达式,可能有副作用,+=1并且++应该快两倍。

许多人专注于这种低级优化,好像这就是优化的全部内容。我假设你知道这是一个更大的主题。

于 2009-05-01T11:51:46.767 回答