如何使用 dyn Trait 并在 Rust 中保持并行性能?

问题描述 投票:0回答:2

为了练习 Rust,我正在实施光线追踪器。

第一步我只使用球体来实现它。我会遍历一个 Vector of Sphere 结构。 我可以通过做

rayon
.
 使用 
into_iter_par

轻松提高性能

现在我正在尝试添加立方体。所以我没有循环

Vec<Sphere>
,而是循环
Vec<Box<dyn Body>>
。这似乎否定了使用人造丝获得的性能。

这种性能损失是如何发生的,我可以修复它吗?

在此代码片段中,我可以重现该行为。

use rayon::prelude::*;
use std::f64::consts::PI;
use std::time::Instant;

pub trait Body: Sync + Send {
    fn size(&self) -> f64 {
        0.
    }
}

#[derive(Clone)]
pub struct Sphere {
    radius: f64,
}

impl Sphere {
    pub fn new() -> Self {
        Sphere { radius: 4.0 }
    }
}

impl Body for Sphere {
    fn size(&self) -> f64 {
        4.0 / 3.0 * PI * self.radius * self.radius * self.radius
    }
}
pub struct World {
    bodies: Vec<Box<dyn Body>>,
    spheres: Vec<Sphere>,
}

impl World {
    fn new() -> Self {
        let mut world = World {
            bodies: vec![],
            spheres: vec![],
        };
        for _ in 0..100000000 {
            world.spheres.push(Sphere::new());
            world.bodies.push(Box::new(Sphere::new()));
        }
        world
    }
}

pub fn main() {
    println!("Creating structs");
    let world = World::new();
    println!("Normal,Spheres");
    let timer = Instant::now();
    world.spheres.iter().map(|s| s.size()).sum::<f64>();
    println!("elapsed time:{:?}", timer.elapsed());

    println!("Normal,Bodies");
    let timer = Instant::now();
    world.bodies.iter().map(|s| s.size()).sum::<f64>();
    println!("elapsed time:{:?}", timer.elapsed());

    println!("Parallel,Spheres");
    let timer = Instant::now();
    world.spheres.into_par_iter().map(|s| s.size()).sum::<f64>();
    println!("elapsed time:{:?}", timer.elapsed());

    println!("Parallel,Bodies");
    let timer = Instant::now();
    world.bodies.into_par_iter().map(|s| s.size()).sum::<f64>();
    println!("elapsed time:{:?}", timer.elapsed());
}


使用发布版本运行时,会产生输出:


Creating structs
Normal,Spheres
elapsed time:97ns
Normal,Bodies
elapsed time:323.960404ms
Parallel,Spheres
elapsed time:88.960257ms
Parallel,Bodies
elapsed time:6.015788183s

rust traits rayon
2个回答
0
投票

你没有使用总和,所以第一个被完全优化掉了。你需要用它做一些事情,比如打印:

let sum = world.spheres.iter().map(|s| s.size()).sum::<f64>();
println!("elapsed time:{:?} ({sum})", timer.elapsed());

这个基准没有意义。

  1. 您没有执行要优化的计算(光线追踪)。您不能通过对总和进行基准测试来对光线追踪进行基准测试。
  2. 光线追踪器通常平行于像素,而不是物体。您也许可以使光栅化器平行于对象,但这对于光线跟踪器来说意义不大。
  3. 您没有比较等效操作。使用
    Box<dyn Body>
    will 会更慢,但它允许您存储您不使用的不同对象。将其与存储枚举或将不同的对象类型存储在不同的
    Vec
    s中进行比较。
  4. iter
    的平行等价物是
    par_iter
    。使用
    into_par_iter
    也会掉落物品,这可能比总和花费更多的时间。

0
投票

我提供的代码片段不是我实际的光线追踪器或基准。这是我尝试在一个片段中重现我的问题。

我在这里得到了答案https://github.com/rayon-rs/rayon/issues/1038

TL:DR 预计会放缓

© www.soinside.com 2019 - 2024. All rights reserved.