Parallel processing 使用带有向量块的横梁作用域线程

Parallel processing 使用带有向量块的横梁作用域线程,parallel-processing,rust,Parallel Processing,Rust,我有一个元组向量,我将其分解成块,然后将每个块交给它自己的线程进行处理,并在最后重新组合它们。 我一直在使用下面的代码,它使用了std::thread,它工作得很好,但是需要大量的克隆和重构,我想消除这些 // convert_bng is just an expensive function let orig: Vec<(&f32, &f32)> // orig gets populated here let mut guards: Vec<JoinHandl

我有一个元组向量,我将其分解成块,然后将每个块交给它自己的线程进行处理,并在最后重新组合它们。 我一直在使用下面的代码,它使用了
std::thread
,它工作得很好,但是需要大量的克隆和重构,我想消除这些

// convert_bng is just an expensive function
let orig: Vec<(&f32, &f32)>
// orig gets populated here
let mut guards: Vec<JoinHandle<Vec<(i32, i32)>>> = vec![];
    // split into slices
    let mut size = orig.len() / NUMTHREADS;
    if orig.len() % NUMTHREADS > 0 {
        size += 1;
    }
    // if orig.len() == 0, we need another adjustment
    size = std::cmp::max(1, size);
    for chunk in orig.chunks(size) {
        let chunk = chunk.to_owned();
        let g = thread::spawn(move || {
            chunk.into_iter()
                 .map(|elem| convert_bng(elem.0, elem.1))
                 .collect()
        });
        guards.push(g);
    }
    let mut result: Vec<IntTuple> = Vec::with_capacity(orig.len());
    for g in guards {
        result.extend(g.join()
                       .unwrap()
                       .into_iter()
                       .map(|ints| {
                           IntTuple {
                               a: ints.0 as u32,
                               b: ints.1 as u32,
                           }
                       }));
    }
(使用shepmaster链接到的问题的代码编辑)

但是,这给了我一个错误,
let result…

将&f32强制转换为u32无效
,这表示
作用域.spawn()内的映射调用未将其结果返回到块中,或者由于类型不匹配而被丢弃(函数返回
(i32,i32)
,但
orig
保持
(&f32,&f32)
)。如果我用一个有效的虚拟向量代替
结果
,我会得到一个与
生成
相关的完全不同的错误:

error: unable to infer enough type information about `_`; type annotations or generic parameter binding required [E0282]
scope.spawn(move || chunk.iter().map(|elem| convert_bng(elem.0, elem.1)).collect());
这表明
scope.spawn()中的映射调用未将其结果返回到块中

这是真的,因为那不是方法的工作原理。一般的想法是建立一个新的作用域,然后可以生成保证在该作用域结束之前结束的线程

crossbeam::scope
建立范围,并:

spawn
接受一个不带参数并返回某种类型的闭包。该类型将是
spawn
方法(对一些错误处理进行模化)的结果

横梁本身不会修改任何数据,这取决于您的代码。让我们看看你在线程中做了什么:

chunk.iter().map(|elem| convert_bng(elem.0, elem.1)).collect()
您获取
&mut[T]
并对其进行迭代,通过将每个元素传递给
convert\bng
来转换它们。您没有
convert\u bng
的定义,因此假设它返回
bool
。然后收集
bool
s的迭代器。然而,有许多可能的收集,你可以针对,所以我们需要知道什么具体的收集是理想的。在大多数情况下,这是通过类似于
let-foo:ConcreteCollection=iter.collect()
的方法完成的

由于此
collect
是最后一个表达式,因此它也是
spawn
的返回值,因此我们可以查看如何使用该返回值。事实并非如此,这相当于只说
iterator.collect(),没有足够的信息进行编译


除此之外,您还需要决定是否尝试就地修改集合。您执行
chunks\u mut
的事实似乎表明您希望每个线程在不进行任何额外分配的情况下对向量执行工作。但是,您忽略了它是可变的,并返回新分配的集合(类型不确定)。因为我不知道convert\u bng
返回什么,所以很难说它是否可能。此外,
map
仅在将类型转换为新类型时使用,而不是在适当位置对值进行变异。您不能在适当的位置对切片进行变异以将不同的类型放入其中,因为它将不再是切片

我目前的方法很有效,就是将convert\u bng重新写入以下签名:
fn convert_bng(经度:&f32,纬度:&f32)->(i32,i32){…}

这样我就可以按如下方式使用横梁:

pub extern "C" fn convert_to_bng(longitudes: Array, latitudes: Array) -> Array {
    let orig: Vec<(&f32, &f32)> = // orig is populated here
    let mut result = vec![(1, 1); orig.len()];
    let mut size = orig.len() / NUMTHREADS;
    if orig.len() % NUMTHREADS > 0 {
        size += 1;
    }
    size = std::cmp::max(1, size);
    crossbeam::scope(|scope| {
        for (res_chunk, orig_chunk) in result.chunks_mut(size).zip(orig.chunks(size)) {
            scope.spawn(move || {
                for (res_elem, orig_elem) in res_chunk.iter_mut().zip(orig_chunk.iter()) {
                    *res_elem = convert_bng(orig_elem.0, orig_elem.1);
                }
            });
        }
    });
    Array::from_vec(result)
}
pub extern“C”fn将_转换为_bng(经度:数组,纬度:数组)->数组{
让orig:Vec=//orig在这里填充
让mut result=vec![(1,1);orig.len();
让mut size=orig.len()/NUMTHREADS;
如果orig.len()%NUMTHREADS>0{
大小+=1;
}
大小=标准::cmp::最大值(1,大小);
横梁::范围(|范围|{
for(res_chunk,orig_chunk)in result.chunks_mut(size).zip(orig.chunks(size)){
范围.繁殖(移动| |{
for res_chunk.iter_mut().zip(orig_chunk.iter()中的(res_elem,orig_elem){
*res_elem=转换(原始元素0,原始元素1);
}
});
}
});
数组::from_vec(结果)
}
此代码更加清晰和简单,运行速度基本上与原始代码相同(平均快<1%)

对我来说,使用预填充的结果向量似乎并不理想,但尝试就地变异
orig
的方法似乎并不奏效:convert\bng的返回类型不可避免地与
orig
不同,目前我无法尝试解决这一问题

fn spawn<F, T>(&self, f: F) -> ScopedJoinHandle<T> 
    where F: FnOnce() -> T + Send + 'a,
          T: Send + 'a
chunk.iter().map(|elem| convert_bng(elem.0, elem.1)).collect()
pub extern "C" fn convert_to_bng(longitudes: Array, latitudes: Array) -> Array {
    let orig: Vec<(&f32, &f32)> = // orig is populated here
    let mut result = vec![(1, 1); orig.len()];
    let mut size = orig.len() / NUMTHREADS;
    if orig.len() % NUMTHREADS > 0 {
        size += 1;
    }
    size = std::cmp::max(1, size);
    crossbeam::scope(|scope| {
        for (res_chunk, orig_chunk) in result.chunks_mut(size).zip(orig.chunks(size)) {
            scope.spawn(move || {
                for (res_elem, orig_elem) in res_chunk.iter_mut().zip(orig_chunk.iter()) {
                    *res_elem = convert_bng(orig_elem.0, orig_elem.1);
                }
            });
        }
    });
    Array::from_vec(result)
}