为什么Rust中的对数比Java中的对数慢?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (28)

如果我在Rust中运行这些基准:

#[bench]
fn bench_rnd(b: &mut Bencher) {
    let mut rng = rand::weak_rng();
    b.iter(|| rng.gen_range::<f64>(2.0, 100.0));
}

#[bench]
fn bench_ln(b: &mut Bencher) {
    let mut rng = rand::weak_rng();
    b.iter(|| rng.gen_range::<f64>(2.0, 100.0).ln());
}

结果是:

test tests::bench_ln             ... bench:        121 ns/iter (+/- 2)
test tests::bench_rnd            ... bench:          6 ns/iter (+/- 0)

每次ln通话121-6 = 115 ns 。

但是Java中的相同基准:

@State(Scope.Benchmark)
public static class Rnd {
    final double x = ThreadLocalRandom.current().nextDouble(2, 100);
}

@Benchmark
public double testLog(Rnd rnd) {
    return Math.log(rnd.x);
}

给我:

Benchmark    Mode Cnt  Score  Error Units
Main.testLog avgt  20 31,555 ± 0,234 ns/op

日志在Rust中比在Java中慢了约3.7倍(115/31)。

当我测试斜边实现(hypot)时,Rust中的实现比Java中快15.8倍。

我写了错误的基准还是性能问题?

提问于
用户回答回答于

答案是@kennytm:

export RUSTFLAGS='-Ctarget-cpu=native'

解决问题。在此之后,结果是:

test tests::bench_ln              ... bench:          43 ns/iter (+/- 3)
test tests::bench_rnd             ... bench:           5 ns/iter (+/- 0)

我认为38(±3)足够接近31.555(±0.234)。

用户回答回答于

由于我不认识Rust,因此我会提供另一半的解释。Math.log注释的@HotSpotIntrinsicCandidate含义是它将被一个本地CPU指令替换为这样的操作:认为Integer.bitCount该操作可能会进行大量的移位操作,或者使用直接的CPU指令,速度更快。

有这样一个非常简单的程序:

public static void main(String[] args) {
    System.out.println(mathLn(20_000));
}

private static long mathLn(int x) {
    long result = 0L;
    for (int i = 0; i < x; ++i) {
        result = result + ln(i);
    }
    return result;
}

private static final long ln(int x) {
    return (long) Math.log(x);
}

并运行它:

 java -XX:+UnlockDiagnosticVMOptions  
      -XX:+PrintInlining 
      -XX:+PrintIntrinsics 
      -XX:CICompilerCount=2 
      -XX:+PrintCompilation  
      package/Classname 

它会产生很多线条,但其中之一是:

 @ 2   java.lang.Math::log (5 bytes)   intrinsic

使这个代码非常快。

扫码关注云+社区