Arc::new() 对于克隆向量很慢

Ble*_*ezz 7 rust

执行此操作后:

use std::sync::Arc;
use std::time::Instant;
fn main() {
    let cap = 100000000;
    let b0 = vec![0; cap];
    let now = Instant::now();
    Arc::new(b0);
    println!("T0: {:?}", now.elapsed());
    let c0 = vec![0; cap];
    let _ = c0.clone(); // <- this makes it slow
    let now = Instant::now();
    Arc::new(c0);
    println!("T1: {:?}", now.elapsed());
}
Run Code Online (Sandbox Code Playgroud)

结果是:T0:5.971µs T1:26.69574ms

如果我们之前克隆 c0,为什么第二个 Arc::new 很慢?

编辑:

我用以下方法测试了它:

  • Windows 10,rust 1.44.1 调试
  • Linux,Rust 1.47 每晚发布
  • Linux,锈 1.18
  • MacOS,Rust 1.44.0 每晚发布

T1 的时间随向量大小线性增加。

Jmb*_*Jmb 5

请注意,您不是在测量 所花费的时间Arc::new,而是测量 Arc 被放下时所花费的时间(因为您没有将其分配给任何东西)。

另请注意,根据您的系统,此行:

let b0 = vec![0; cap];
Run Code Online (Sandbox Code Playgroud)

可能不分配任何物理内存:它只能分配虚拟空间,物理内存在第一次访问时被分配和归零。cachegrind 证实了这一点,在克隆缓冲区之前几乎没有缓存未命中。

克隆载体有两个副作用:

  • 它导致物理内存被映射,这意味着它必须在删除时取消映射Arc
  • 它会破坏缓存(由于内存清零和复制),从而导致后续代码中的缓存未命中。

使用以下代码将解除分配移出测量的时间,时间更快更接近:

use std::sync::Arc;
use std::time::Instant;

fn main() {
   let cap = 1000000000;
   let b0 = vec![0; cap];
   let now = Instant::now();
   let a = Arc::new(b0);
   println!("T0: {:?}", now.elapsed());
   drop (a);
   let c0 = vec![0; cap];
   let _ = c0.clone(); // <- this makes it slow
   let now2 = Instant::now();
   let a = Arc::new(c0);
   println!("T1: {:?}", now2.elapsed());
   drop (a);
}
Run Code Online (Sandbox Code Playgroud)

第二个Arc::new仍然较慢,但可以通过 cachegrind 报告的 3 个额外的 L3 缓存未命中来解释差异。