首页 > 解决方案 > Rust 中大量线程的性能下降

问题描述

我正在HashMapRust 中创建一个无闩锁并发。吞吐量曲线看起来和我预期的一样,大约有 16 个线程,此时性能会下降。

吞吐量 (MOps/sec) 与线程数

吞吐量 (MOps/sec) 与线程数

我使用了一个具有 48 个 vCPU 和 200GB RAM 的 Google Cloud 实例。我尝试启用/禁用超线程,但没有明显的结果。

这是我生成线程的方式:

for i in 0..num_threads {
    //clone the shared data structure
    let ht = Arc::clone(&ht);

    let handle = thread::spawn(move || {
        for j in 0..adds_per_thread {
            //randomly generate and add a (key, value)
            let key = thread_rng().gen::<u32>();
            let value = thread_rng().gen::<u32>();
            ht.set_item(key, value);
        }
    });

    handles.push(handle);
}

for handle in handles {
    handle.join().unwrap();
}

我没主意了;我的 Rust 代码是否适用于多线程?

标签: multithreadingperformancerusthashmaplock-free

解决方案


如果你所有的线程都花时间在你的无锁数据结构上,是的,一旦你有足够的线程,你就会发生争用。有了足够多的写入者,他们会更频繁地争夺表中的同一缓存行。(此外,在 PRNG 中花费的时间可能不会隐藏共享带宽到缓存或 DRAM 的争用)。

您可能会开始遇到更多的 CAS 重试和类似的事情,包括任何争用退避机制,而不仅仅是一个平台期。此外,线程将遭受缓存未命中,甚至从一些原子读取中清除内存顺序错误推测管道;并非所有内容都是原子 RMW 或写入。

这不是无锁数据结构的正常用例;通常,您将它们与执行重要工作的代码一起使用,而不是锤击它们,因此实际争用很少。此外,hashmap 的实际工作负载很少是只写的(尽管如果您只想删除重复数据,可能会发生这种情况)。

读取与读者数量的比例非常好,但写入会发生争用。


推荐阅读