当前位置: 首页 > 知识库问答 >
问题:

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

梁骞仕
2023-03-14

如果我在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)

每次通话121-6=115纳秒。

但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倍。

我是否编写了糟糕的基准测试,或者这是一个性能问题?

回复评论中提出的问题:

>

我使用总是在发布模式下运行的cargo bench运行Rust的基准测试。

Java基准测试框架(JavaBenchmarkFramework,JMH)为每个调用创建一个新对象,即使它是静态类和最终变量。如果我在测试方法中添加一个随机创建,我得到43 ns/op。

共有2个答案

翁硕
2023-03-14

我将提供另一半的解释,因为我不知道<代码>数学。日志用@hospotintrisccandidate注释,这意味着对于这样的操作,它将被本机CPU指令替换:thinkInteger。位计数可以进行大量移位,也可以使用速度更快的直接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

使这个代码非常快。

我真的不知道这是什么时候发生的,是怎么发生的。。。

董高畅
2023-03-14

答案由@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)。

 类似资料:
  • 问题内容: 为什么比Java 慢?换句话说,为什么优于? 当我查看中的实现时,速度似乎应该是相同的: ArrayList.size() ArrayList.isEmpty() 如果我们只编写一个简单的程序来获取两种方法所花费的时间,那么在所有情况下这种情况都将花费更多,为什么会这样呢? 这是我的TestCode; 在所有情况下都在这里。为什么? 问题答案: 您的测试代码有缺陷。 只需颠倒顺序,即先

  • 我在Surface Pro 2平板电脑上运行Windows8.1x64和Java7更新45x64(没有安装32位Java)。 下面的代码在i类型为long时占用1688ms,在i类型为int时占用109ms。为什么在64位JVM的64位平台上,long(64位类型)比int慢一个数量级? 我唯一的猜测是,CPU加一个64位整数比加一个32位整数需要更长的时间,但这似乎不太可能。我怀疑Haswell

  • 我正在学习Java,刚刚发现了这个关于该语言的微妙事实:如果我声明两个具有相同元素的整数数组,并使用比较它们,结果是。为什么会出现这种情况?比较值不应该为吗? 提前感谢!

  • 问题内容: 从2010年的计算机语言基准游戏中可以看出: Go平均比C慢10倍 Go比Java慢3倍! 考虑到Go编译器会生成要执行的本机代码,这怎么可能? Go的编译器不成熟?还是Go语言存在一些内在问题? 编辑: 大多数答案否认Go语言的内在缓慢,声称问题出在不成熟的编译器中。 因此,我进行了一些自己的测试来计算斐波那契数:迭代算法在Go(freebsd,6g)中以与C(带有O3选项)一样的速

  • 问题内容: 在调查使用以及将整数原语转换为字符串的简短辩论时,我编写了以下JMH微基准测试: 我在Linux机器上的两个Java VM上都使用默认的JMH选项运行了它(最新的Mageia 4 64位,Intel i7-3770 CPU,32GB RAM)。第一个JVM是Oracle JDK 8u5 64位提供的: 有了这个JVM,我得到了我所期望的: 也就是说,由于创建对象和附加空字符串会产生额外

  • 问题内容: 输出: 为什么这样的输出?我期望作为第一种情况的结果。 问题答案: 区别在于6.5可以完全以float和double表示,而3.2不能完全以两种类型表示。并且两个最接近的近似是不同的。 float和double之间的相等比较首先将float转换为double,然后将两者进行比较。因此数据丢失。 您不应该比较浮点数或双精度数是否相等。因为您不能真正保证分配给float或double的数字