在Java中,使用"int a=a+1"和"a++"的性能差异是什么?

8

在Java中,使用int a = a + 1a++是否有性能差异?

如果有,哪个更好,为什么?您能简要解释一下让我理解吗?


4
考虑微小优化:始终选择更易读的方式,让编译器为你进行优化。代码是给程序员看的,而优化是给编译器做的。 - amit
4
提出这个问题所花费的时间比较快的解决方案(如果有的话)带来的运行时间优势更多。 - Bernhard Kircher
2
@Bernhard:我只是想知道是否有任何区别。 - Surya Chandra
6个回答

15
首先,Java语言规范没有提及时间问题。但是假设我们使用类似于Sun的javac编译器,我们可以看到以上所有示例(`a++`,`++a`,`a+=1`,`a=a+1`)都可以被编译成类似以下的内容: (以下为代码示例)

iinc指令,作用于变量:

    iload_<variable>
    iinc <variable>, 1
    istore_<variable>
    
  • 使用堆栈的iadd指令(这里将变量1用作存储器):

  • iload_1
    iconst_1
    iadd
    istore_1
    
    编译器会选择最佳的编译方式。例如,它们之间没有区别,并且语句之间不应该有任何区别-它们都表达了同样的意思-将一个数字加一。
    也就是说,无论是iinc 还是iadd版本,都可以使用JIT编译为快速和平台相关的代码,并且最终我会假设普通运行时将两个版本都编译成相同的汇编代码。
    使用我的编译器*jdk1.6.0_20*,"increment"方法甚至使用相同的指令。
    public class Test {
        public static void main(String[] args) {
    
            int a = 0;
    
            a = a + 1;
            a += 1;
            a++;
            ++a;
        }
    }
    

    这是反汇编代码:

    Compiled from "Test.java"
    public class Test extends java.lang.Object{
    public Test();
      Code:
       0:   aload_0
       1:   invokespecial   #8; //Method java/lang/Object."<init>":()V
       4:   return
    
    public static void main(java.lang.String[]);
      Code:
       0:   iconst_0
       1:   istore_1
       2:   iinc    1, 1   // a = a + 1;
       5:   iinc    1, 1   // a += 1;
       8:   iinc    1, 1   // a++;
       11:  iinc    1, 1   // ++a;
       14:  return
    
    }
    

请问您能否为++a添加更多细节信息? - Azodious
@Azodious:这取决于情况,如果我只是在上述语句中添加++a;,它只会将其转换为另一个iinc 1, 1。然而,在更复杂的情况下,指令的顺序是不同的(参见此SO帖子)。 - dacwe
我认为在“iinc指令,处理变量”这一部分存在错误。iinc不需要iloadistore吗? - Johannes

3

查看生成的字节码:

public static void main(String[] args) {
    int x = 1;
    int y = 1;
    int z = 1;
    int a = 1;
    int b = 1;
    x = x + 1;
    y++;
    ++z;
    a += 1;
    b += 2;
}

生成(使用javap -c classname
0:   iconst_1
1:   istore_1
2:   iconst_1
3:   istore_2
4:   iconst_1
5:   istore_3
6:   iconst_1
7:   istore  4
9:   iconst_1
10:  istore  5
12:  iload_1
13:  iconst_1
14:  iadd
15:  istore_1
16:  iinc    2, 1
19:  iinc    3, 1
22:  iinc    4, 1
25:  iinc    5, 2
28:  return

因此,使用(jdk1.6.0_18):

x = x + 1

创建
12:  iload_1
13:  iconst_1
14:  iadd
15:  istore_1

y++;
++z;
a += 1;

所有结果都在以下内容中呈现:
iinc

然而,在我的笔记本电脑上进行了一次粗略的性能测试,结果两者之间的运行时间几乎没有区别(有时++x更快,有时x=x+1更快),因此我不会担心性能方面的影响。


1
你应该从中学到的教训是,你不能通过计算字节码来推断Java的性能!(即使你使用java -int ...运行以关闭JIT编译。) - Stephen C

2

不会有明显的区别。使用你觉得最易读的方式(通常是 a++)。

代码优化的第一条规则:不要优化。


1
我没有点踩,但实际上我不同意你的观点。从清晰的代码角度来看,a+=1 至少在我的角度来看更加稳定,无论是可读性还是容错性。你有没有评估过 a+++b(在Java和C++中)?唯一支持 a++ 的论点是不同的处理器指令,但这已经被JVM优化了。 - fyr
我们讨论的不是a+++b,而是a++,它是指“增加a”的指令。任何Java开发人员都可以读懂它,这个指令没有理解上的问题。 - JB Nizet
这正是每个人都应该理解的要点。但它并不是说“增加a”。 a++ 的意思是“在当前表达式之后增加a”。 - fyr
仅当表达式只是 a++ 时,才不会有赋值。在这种情况下,增量是在分配之前还是之后完成并不重要:不存在赋值。这就是 OP 的问题:a = a + 1 还是 a++ - JB Nizet

1
编译器应该进行优化,因此不应该有任何区别。但请记住,在C++和C#中,前缀递增运算符可能比后缀等效运算符更快(这取决于编译器): ++aa++ 更快,因为后缀运算符必须创建一个临时变量.. 请考虑它们的实现:
前缀:
a = a + 1;
return a;

后缀表达式:

int tmp = a;
a = a + 1;
return tmp;

nop:https://dev59.com/questions/uXVD5IYBdhLWcg3wTJvF - assylias
就像我说的那样:“可能”。不过,并非所有编译器都会进行这种优化。 - vulkanino
@vulkanino:你必须阅读:https://dev59.com/LW445IYBdhLWcg3w6eVo - fyr

0

a++更快。它转换为汇编的INC命令。但我认为JVM会优化a=a+1,所以你不需要担心。


0

这是一样的,而且现在编译器优化应该不需要关注这些东西,要提高性能,请检查其他更大的问题,比如分配内存等 :)


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