【问题标题】:Performance of Arithmetic Compound Assignment Operators in JavaJava中算术复合赋值运算符的性能
【发布时间】:2016-09-02 13:18:10
【问题描述】:

我在基准测试中注意到,Java 中的算术复合运算符总是优于常规赋值:

    d0 *= d0;           //faster
    //d0 = d0 * d0;     //slower

    d0 += d0;           //faster
    //d0 = d0 + d0;     //slower

有人可以对上述观察发表评论并解释为什么会这样。我假设字节码级别的一些差异是导致加速的原因?提前谢谢你。

这是我的更全面的基准:

public long squaring() {
    long t0 = System.currentTimeMillis();
    double d0 = 0;

    for (int k = 0; k < 100_000_000; k++){

        //check bytecode for below to see why timing differs
        d0 *= d0;           //faster
        //d0 = d0 * d0;     //slower
    }

    long t1 = System.currentTimeMillis();
    long took = (t1 - t0);
    System.out.println("took: "+took + " ms");
    System.out.println("result: " +d0);

    return took;
}

@Test
    public void testSquaring() {
        int repetitions = 10;
        long sum = 0;
        for (int i = 0; i < repetitions; i++) {
            sum += cut.squaring();
            System.out.println("accumulated: "+ sum + "\n-------------------");
        }
        double avg = sum/repetitions;
        System.out.println("average: "+avg);

    }

结果如下:

took: 244 ms
result: 0.0
accumulated: 244
-------------------
took: 302 ms
result: 0.0
accumulated: 546
-------------------
took: 0 ms
result: 0.0
accumulated: 546
-------------------
took: 0 ms
result: 0.0
accumulated: 546
-------------------
took: 0 ms
result: 0.0
accumulated: 546
-------------------
took: 0 ms
result: 0.0
accumulated: 546
-------------------
took: 0 ms
result: 0.0
accumulated: 546
-------------------
took: 0 ms
result: 0.0
accumulated: 546
-------------------
took: 0 ms
result: 0.0
accumulated: 546
-------------------
took: 0 ms
result: 0.0
accumulated: 546
-------------------
average: 54.0

【问题讨论】:

  • 注释掉的代码执行两次乘法,而不是一次。这真的是你要比较的吗?
  • 你可以只看字节码来验证你的假设。
  • 也就是说,你衡量这个的方式很有可能会产生误导 - 请参阅 stackoverflow.com/questions/504103/…
  • 那些似乎产生完全相同的字节码。我认为问题在于你的测试。
  • “字节码级别的一些差异”javap -c YourClass 向您显示字节码。

标签: java performance syntax


【解决方案1】:

尝试反编译这两种情况:

static void a(double d0) {
  d0 *= d0;
}

static void b(double d0) {
  d0 = d0 * d0;
}

反编译为:

  static void a(double);
    Code:
       0: dload_0
       1: dload_0
       2: dmul
       3: dstore_0
       4: return

  static void b(double);
    Code:
       0: dload_0
       1: dload_0
       2: dmul
       3: dstore_0
       4: return

即它们在字节码级别是相同的,因此不会有内在的性能差异。此代码之外的因素正在影响运行时间。

【讨论】:

    【解决方案2】:

    a *= b; 产生与a = a * b; 完全相同的字节码。这意味着您的性能测试存在问题。

    同样适用于+-/ 等。

    【讨论】:

    • 我发布了更全面的测试——你能发现那里有什么问题吗?:
    • 您对大多数测试打印took: 0 ms 感到惊讶吗?只有两个返回有意义的东西。
    • 我认为是因为 JIT 在 2 次尝试后对其进行了优化?
    • @SimeonLeyzerzon 是的。在这个优化之后,没有进一步的调用对你的结果有贡献。所以你主要测量 JIT 这个方法需要多长时间,但你想测量其他东西,对吧?
    • @SimeonLeyzerzon Java 中的微基准测试很困难,原因有很多。我建议您找到一些用于微基准测试的框架并使用它,而不是尝试自己处理所有复杂性。
    猜你喜欢
    • 2011-11-16
    • 1970-01-01
    • 2016-04-12
    • 2020-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多