今天,我遇到了BigDecimal的一种奇怪的性能行为。简而言之,以下两段试图做同一件事的代码之间存在显着差异

int hash = foo();
BigDecimal number = new BigDecimal(hash);


BigDecimal number = new BigDecimal(foo());

为了证明这一点,我在下面的 class 中展示了差异。我的Java是1.7.0_75-b13,64位,mac。在我的环境中,第一个循环花费2s,第二个循环花费5s。
import java.math.BigDecimal;

public class Crazy {

public static void main(String[] args) {
    new Crazy().run();
}

void run() {
    // init

    long count = 1000000000l;

    // start test 1

    long start = System.currentTimeMillis();

    long sum = 0;
    for (long i=0; i<count; i++) {
        sum = add(sum);
    }

    long end = System.currentTimeMillis();
    System.out.println(end - start);

    // start test 2

    long start2 = end;
    sum = 0;
    for (long i=0; i<count; i++) {
        sum = add1(sum);
    }

    long end2 = System.currentTimeMillis();
    System.out.println(end2 - start2);
}

long add(long sum) {
    int hash = hashCode();
    BigDecimal number = new BigDecimal(hash);
    sum += number.longValue();
    return sum;
}

long add1(long sum) {
    BigDecimal number = new BigDecimal(hashCode());
    sum += number.longValue();
    return sum;
}
}

javap输出
long add(long);
Code:
   0: aload_0
   1: invokevirtual #56                 // Method java/lang/Object.hashCode:()I
   4: istore_3
   5: new           #60                 // class java/math/BigDecimal
   8: dup
   9: iload_3
  10: invokespecial #62                 // Method java/math/BigDecimal."<init>":(I)V
  13: astore        4
  15: lload_1
  16: aload         4
  18: invokevirtual #65                 // Method java/math/BigDecimal.longValue:()J
  21: ladd
  22: lstore_1
  23: lload_1
  24: lreturn

long add1(long);
Code:
   0: new           #60                 // class java/math/BigDecimal
   3: dup
   4: aload_0
   5: invokevirtual #56                 // Method java/lang/Object.hashCode:()I
   8: invokespecial #62                 // Method java/math/BigDecimal."<init>":(I)V
  11: astore_3
  12: lload_1
  13: aload_3
  14: invokevirtual #65                 // Method java/math/BigDecimal.longValue:()J
  17: ladd
  18: lstore_1
  19: lload_1
  20: lreturn

最佳答案

我无法重现。考虑以下Microbenchmark:

@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public class BigDecimalBenchmark {

  static int i = 1024;

  @Benchmark
  public BigDecimal constructor() {
    return new BigDecimal(foo());
  }

  @Benchmark
  public BigDecimal localVariable() {
    int hash = foo();
    return new BigDecimal(hash);
  }

  private static int foo() {
    return i;
  }

}

给出以下输出:
Benchmark                             Mode  Samples       Score      Error   Units
BigDecimalBenchmark.constructor      thrpt      100  180368.227 ± 4280.269  ops/ms
BigDecimalBenchmark.localVariable    thrpt      100  173519.036 ±  868.547  ops/ms

更新

编辑基准以使foo()不可内联。

07-24 09:37