2017-07-11 46 views
16

Ben Rust bu kriterleri çalıştırırsanız:Logaritma neden Java'da Java'dan daha yavaş?

#[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()); 
} 

sonucudur: ln çağrı başına

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

121-6 = 115 ns.

Ama Java aynı kriter:

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

bana verir:

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

günlük olduğunu ~ Java daha Rust (115/31) 3,7 kat daha yavaş.

Hipotenüs uygulamasının (hypot) test edildiğinde, Rust uygulamasının Java'ya göre 15,8 katı daha hızlıdır.

Kötü kriterler yazdım ya da bir performans sorunu mu var? yorumlarda sorulan sorulara

Tepkiler:

  1. "" Ülkemde bir ondalık ayırıcı olduğunu.

  2. Rust'un benchmarkını her zaman yayın modunda çalışan cargo bench kullanarak çalıştırıyorum.

  3. Java kriter çerçevesi (JMH) bir static sınıf ve final değişken olmasına rağmen, her çağrı için yeni bir nesne oluşturur. Test edilen yönteme rastgele bir yaratım eklerseniz, 43 ns/op elde ederim.

+1

Isnt java kriter temel olarak kullanmak ... Gerçekten ne zaman bilmiyorum ve nasıl olsa Rust olur? Yani java güzel ama bazı durumlarda çok güzel – Wietlol

+3

Muhtemelen rasgele sayı üretecini log fonksiyonundan daha fazla kıyaslıyorsunuzdur. Ayrıca, pasın sadece sistem matematik kütüphanesini kullandığına inanıyorum, bu yüzden “log” için saf bir çağrı C'deki ile aynı olmalıdır (Java hakkında hiçbir fikir). –

+2

Testi "RUSTFLAGS =" - Ctarget-cpu = native 'cargo bench 'kullanarak yeniden çalıştırabilirsiniz. – kennytm

cevap

7

cevap given by @kennytm oldu:

export RUSTFLAGS='-Ctarget-cpu=native' 

Düzeltmeler sorunu. Bundan sonra, sonuçlar şunlardır:

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

I (0.234 ±) 38 (± 3) 31.555 yeterince yakın olduğunu düşünüyorum.

+0

Halen şaşırtıcı bir şekilde java kodundan daha yavaştır. – Boiethios

6

Açıklamaları bilmediğimden bu açıklamaların diğer yarısını sunacağım. Math.log, @HotSpotIntrinsicCandidate ile açıklanmıştır, yani böyle bir işlem için yerel bir CPU talimatı ile değiştirilecektir: Integer.bitCount düşünün, ya çok fazla vardiya yapar ya da daha hızlı bir şekilde doğrudan CPU komutunu kullanır.

böyle son derece basit bir programı olması:

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

Ve onu çalıştıran:

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

Bu satırları bir çok oluşturacak ancak bunlardan biridir:

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

bu kodu son derece hızlı bir şekilde yapıyor.

kötü

+7

Rust, statik olarak (ya da isterseniz AOT) derlendiği için, derlemek için tek bir platform bilmek zorundadır. Varsayılan olarak, bu tür bir muhafazakar olacaktır (örneğin, 32 bit x86 kodu 686 işlemciyi hedefleyebilir). '-Ctarget-cpu = native' bayrağı, derleyicinin derleyicinin çalıştığı makineyi hedeflemesini söyler; Bu, derleyicinin mevcut komutların tamamını kullanmasına izin verir ('popcnt' örneğiniz gibi). – Shepmaster