gpt4 book ai didi

Java BigDecimal 奇怪的性能行为

转载 作者:塔克拉玛干 更新时间:2023-11-02 08:43:30 24 4
gpt4 key购买 nike

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

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

对比

BigDecimal number = new BigDecimal(foo());

为了证明这一点,我在下面的类(class)中展示了差异。我的 java 是 1.7.0_75-b13,64 位,mac。在我的环境中,第一个循环用了 2 秒,第二个循环用了 5 秒。

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;
}
}

Java 输出

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

最佳答案

我使用以下基准重现了 Java 1.7.0.79 上的效果:

import java.math.BigDecimal;
import java.util.concurrent.TimeUnit;

import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.annotations.*;

@Warmup(iterations = 5, time = 3, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 10, time = 3, timeUnit = TimeUnit.SECONDS)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Fork(2)
@State(Scope.Benchmark)
public class AddTest {
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;
}

@Benchmark
public void testAdd(Blackhole bh) {
long count = 100000000l;
long sum = 0;
for (long i=0; i<count; i++) {
sum = add(sum);
}
bh.consume(sum);
}

@Benchmark
public void testAdd1(Blackhole bh) {
long count = 100000000l;
long sum = 0;
for (long i=0; i<count; i++) {
sum = add1(sum);
}
bh.consume(sum);
}
}

结果如下:

# JMH 1.9 (released 40 days ago)
# VM invoker: C:\Program Files\Java\jdk1.7.0_79\jre\bin\java.exe
# VM options: <none>

Benchmark Mode Cnt Score Error Units
AddTest.testAdd avgt 20 214.740 ± 4.323 ms/op
AddTest.testAdd1 avgt 20 1138.269 ± 32.062 ms/op

有趣的是,使用 1.8.0.25 结果正好相反:

# JMH 1.9 (released 40 days ago)
# VM invoker: C:\Program Files\Java\jdk1.8.0_25\jre\bin\java.exe
# VM options: <none>

Benchmark Mode Cnt Score Error Units
AddTest.testAdd avgt 20 1126.126 ± 22.120 ms/op
AddTest.testAdd1 avgt 20 217.145 ± 1.905 ms/op

但是在 1.8.0_40 上两个版本都很快:

# JMH 1.9 (released 40 days ago)
# VM invoker: C:\Program Files\Java\jdk1.8.0_40\jre\bin\java.exe
# VM options: <none>

Benchmark Mode Cnt Score Error Units
AddTest.testAdd avgt 20 218.925 ± 5.093 ms/op
AddTest.testAdd1 avgt 20 217.066 ± 1.427 ms/op

在所有这些情况下,addadd1 方法都内联到调用方方法中。似乎它只与 JIT 编译器中循环展开机制的内部变化有关:有时你的循环很好地展开,有时不是。

关于Java BigDecimal 奇怪的性能行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30474381/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com