Java 性能竟然优于 Rust,哪里有问题呢?

227 天前
 acr0ss

近日 B 站看到概率问题:《连续抛 10000 次硬币,最多几连正的概率最大?》

使用擅长的 Java 模拟,共 10w 次实验,每次实验模拟投掷 1w 次,耗时 1080ms ;多次运行耗时相差不大。
同样的算法,用 Kimi 翻译成 Rust ,cargo build --release 生成可执行文件;但执行效率不如 Java ,且耗时 10x
哪里有问题呢?

Java

public class TossStreakProbability {
    public static final int tosses = 10_000; // 10^4
    public static final int iterations = 100_000; // 10^5

    public static void main(String[] args) {
        Instant start = Instant.now();
        Map<Integer, Long> streakMap = new HashMap<>();

        for (int i = 0; i < iterations; i++) {
            int maxStreak = maxStreak();
            streakMap.put(maxStreak, streakMap.getOrDefault(maxStreak, 0L) + 1);
        }

        long total = streakMap.values().stream().mapToLong(Long::intValue).sum();
        streakMap.forEach((key, value) -> System.out.printf(
                "Max: %d, count: %d, percent:%.2f%%\n",
                key, value, (value * 100.0) / total));

        // print execute time in ms
        System.out.println("Execution time: " + (Instant.now().toEpochMilli() - start.toEpochMilli()) + "ms");
    }

    public static int maxStreak() {
        int streak = 0, maxStreak = 0;
        var random = ThreadLocalRandom.current();
        for (int i = 0; i < tosses; i++) {
            boolean current = random.nextBoolean();
            if (current) {
                streak++;
            } else {
                streak = 0;
            }
            maxStreak = Math.max(maxStreak, streak);
        }
        return maxStreak;
    }
}

Rust

use std::collections::HashMap;
use std::time::Instant;

use rand::prelude::*;


const TOSSES: i32 = 10_000; // 10^4
const ITERATIONS: i32 = 100_000; // 10^5

fn main() {
    let start = Instant::now();
    let mut streak_map: HashMap<i32, i64> = HashMap::new();

    for _ in 0..ITERATIONS {
        let max_streak = max_streak();
        *streak_map.entry(max_streak).or_insert(0) += 1;
    }

    let total: i64 = streak_map.values().sum();
    for (key, value) in &streak_map {
        println!("Max: {}, count: {}, percent: {:.2}%", key, value, (*value as f64 / total as f64) * 100.0);
    }

    // print execute time in ms
    let duration = start.elapsed();
    println!("Execution time: {}ms", duration.as_millis());
}

fn max_streak() -> i32 {
    let mut streak = 0;
    let mut max_streak = 0;
    let mut rand = thread_rng();

    for _ in 0..TOSSES {
        let current = rand.gen_bool(0.5);
        if current {
            streak += 1;
        } else {
            streak = 0;
        }
        max_streak = std::cmp::max(max_streak, streak);
    }
    max_streak
}
5577 次点击
所在节点    程序员
28 条回复
lvlongxiang199
227 天前
打个火焰图看看 ?
Ericcccccccc
227 天前
看不起 jit 吗...

很多时候性能比 c++ 都高
DAM
227 天前

有外网消息称 m4 替换了 AMX 为 Arm 的 SME ,排除这个因素的 IPC 提升为 0 ;
消息来源 https://twitter.com/negativeonehero/status/1788360191471431919
DAM
227 天前
回复错了帖子😨
qzh993
227 天前
抽时间摸鱼测试了下,没复现呀

UxwVI042kEc5pNx6
227 天前
试了下 Go ,不用协程要 6130ms 左右,用了协程跟 Java 差不多,1110ms 左右:

```go
package main

import (
"fmt"
"math/rand"
"sync"
"time"
)

const (
tosses = 10_000 // 1e4
iterations = 100_000 // 1e5
)

func main() {
start := time.Now()
// streakMap := make(map[int]int64) // 6130±20
streakMap := sync.Map{} // 1110±20

var wg sync.WaitGroup
for i := 0; i < iterations; i++ {
wg.Add(1)
go func() {
defer wg.Done()

max := maxStreak()
//streakMap[max] = streakMap[max] + 1
m, _ := streakMap.Load(max)
if m == nil {
m = int64(0)
}
streakMap.Store(max, m.(int64)+1)
}()
}
wg.Wait()

var total int64
//for _, value := range streakMap {
// total += value
//}
streakMap.Range(func(key, value any) bool {
if value == nil {
value = int64(0)
}
total += value.(int64)
return true
})

//for key, value := range streakMap {
// fmt.Printf("Max: %d, count: %d, percent: %.2f%%\n", key, value, (float64(value)*100)/float64(total))
//}
streakMap.Range(func(key, value any) bool {
fmt.Printf("Max: %d, count: %d, percent: %.2f%%\n", key, value, (float64(value.(int64))*100)/float64(total))
return true
})

// print execute time in ms
fmt.Printf("Execution time: %dms\n", time.Since(start).Milliseconds())
}

func maxStreak() (max int) {
var streak int

var r = rand.New(rand.NewSource(time.Now().UnixNano()))
for i := 0; i < tosses; i++ {
current := r.Intn(2) == 1
if current {
streak++
} else {
streak = 0
}
if streak > max {
max = streak
}
}
return max
}

```
xgdgsc
227 天前
mmdsun
226 天前
Java 预热后也转成机器指令了,速度不一定比 rust 慢

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1039069

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX