【问题标题】:What's could be the right test case for evaluating assignment operators performance vs concrete operations?什么是评估赋值运算符性能与具体操作的正确测试用例?
【发布时间】:2010-08-10 07:19:08
【问题描述】:

我只是想弄清楚分配像

这样的值的快速方法是什么
somevar+=1;

somevar=somevar+1;

以前在使用文本而不是整数的情况下,我使用text+="sometext" 而不是text.append("sometext")text=text+"sometext" 遇到了一些性能下降,问题是我再也找不到我注释我的注意事项的源代码。那么理论上最快的方法是什么?

代码背景设置为快速循环,几乎是实时的。

【问题讨论】:

    标签: java performance operators


    【解决方案1】:

    如果你有这样的事情:

    Collection<String> strings = ...;
    String all = "";
    for (String s : strings) all += s;
    

    ...那么就相当于:

    Collection<String> strings = ...;
    String all = "";
    for (String s : strings) all = new StringBuilder(all).append(s).toString();
    

    每个循环都会创建一个新的StringBuilder,它本质上是all 的副本,将s 的副本附加到它,然后将连接的结果复制到一个新的String。显然,使用单个 StringBuilder 可以节省很多不必要的分配:

    Collection<String> strings = ...;
    StringBuilder sb = new StringBuilder();
    for (String s : strings) sb.append(s);
    String all = sb.toString();
    

    对于x += yx = x + y,它们编译成相同的东西。

    class Concat {
    
            public String concat1(String a, String b) {
                    a += b;
                    return a;
            }
    
            public String concat2(String a, String b) {
                    a = a + b;
                    return a;
            }
    
    }
    

    javac编译,然后用javap反汇编:

    $ javap -c Concat
    Compiled from "Concat.java"
    class Concat extends java.lang.Object{
    Concat();
      Code:
       0:   aload_0
       1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
       4:   return
    
    public java.lang.String concat1(java.lang.String, java.lang.String);
      Code:
       0:   new     #2; //class java/lang/StringBuilder
       3:   dup
       4:   invokespecial   #3; //Method java/lang/StringBuilder."<init>":()V
       7:   aload_1
       8:   invokevirtual   #4; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
       11:  aload_2
       12:  invokevirtual   #4; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
       15:  invokevirtual   #5; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
       18:  astore_1
       19:  aload_1
       20:  areturn
    
    public java.lang.String concat2(java.lang.String, java.lang.String);
      Code:
       0:   new     #2; //class java/lang/StringBuilder
       3:   dup
       4:   invokespecial   #3; //Method java/lang/StringBuilder."<init>":()V
       7:   aload_1
       8:   invokevirtual   #4; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
       11:  aload_2
       12:  invokevirtual   #4; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
       15:  invokevirtual   #5; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
       18:  astore_1
       19:  aload_1
       20:  areturn
    
    }
    

    就个人而言,我更喜欢+=,因为有了它,您可以更清楚地表达意图——“我想将b 的内容添加到a”。两种形式之间的任何性能差异都 100% 确定是代码之外的结果(例如,GC 暂停、随机缓存未命中或类似情况)。

    糟糕的编译器也可能更容易优化+= 表单(这与您无关,因为即使javac 很糟糕,HotSpot 肯定不会)。

    【讨论】:

      【解决方案2】:

      字符串是不可变的。因此,连接 2 个字符串会导致创建一个新的字符串对象。使用 StringBuilder 将获得很多性能。

      但是,原始数字类型不是不可变的,也不是堆分配的。无论如何,你对它们做的任何事情都会非常快:)

      【讨论】:

      • 谢谢!非常有趣的答案
      • 将 2 个 String+ 连接起来非常昂贵,应该避免使用,尤其是在循环中。确保尽可能使用基本类型,例如 int 而不是 Integer
      • @f1sh:实际上,与+ 的字符串连接在许多情况下并不比StringBuilder 贵。我确定知道这一点,但对于所有不成熟的优化器来说,总是值得指出:使用StringBuilder 而不是+ 的主要原因是如果你正在做非一个-出于某种原因建立字符串的衬里,例如在循环中或代码真的很长时。说new StringBuilder("Feces occurred: (").append(errMsg).append(", code = ").append(errCode).append(")").toString() 并不比用+ 做同样的事情快,只是可读性差很多。
      【解决方案3】:

      字符串“算术”内部相当复杂 - 只是因为它们经常发生并且编译器会尽可能优化。

      首先,append("something") 不是String 方法。它是来自StringBufferStringBuilder 类的方法。

      其他表达式是相当的。我很确定人们无法查看该模式并确定总体上更快。

      一般来说,因为字符串是不可变的,连接将为每个字符串字面量创建一个新字符串,为每个中间结果创建一个新字符串,为结果创建一个新字符串。所以这个

      String s = "one" + "two" + "three";
      

      将需要五个字符串对象(“one”、“two”、“three”、“onetwo”、“onwtwothree”)。

      第一个编译器优化是“实习”文字。简而言之:“一”、“二”和“三”不是创建而是重用。

      第二个编译器优化是用 StringBuilder 操作替换 String 添加(如果有意义的话)。所以连接可以替换为

      StringBuilder sb = new StringBuilder("one");
      sb.append("two");
      sb.append("three");
      String s = sb.toString();
      

      (只是为了说明 - 一个真正的编译器可能不会使用此示例代码的策略,它可能已经在编译期间连接文字......)

      【讨论】:

        猜你喜欢
        • 2016-12-27
        • 1970-01-01
        • 2014-11-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-08-25
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多