如何并行化此代码?

时间:2015-08-19 21:01:23

标签: parallel-processing rust

我最初在ruby中写了这个,然后发现MRI根本不支持并行执行。所以我用铁锈重写了它,但我对各种碎片的所有权都有困难。

extern crate rand;
extern crate csv;
extern crate num_cpus;

use std::fs::File;
use csv::Reader;
use rand::{thread_rng, sample};
use std::thread;
use std::str::from_utf8;
use std::io::{self, Write};
use csv::index::{Indexed, create_index};

fn met_n_in_common(n:usize,csv:&mut Reader<File>)->usize{
    csv.byte_records().map(|r| if(from_utf8(r.unwrap().get(n).unwrap()).unwrap() == "TRUE"){1}else{0}).fold(0usize, |sum, i| sum + i)
}

fn mets_in_common(csv:&mut Reader<File>,current_set_length:usize)->usize {
    (0..csv.headers().unwrap().len()).map(|i| if(i == 0){0}else{met_n_in_common(i,csv)} ).filter(|&e| e==current_set_length ).count()
}

fn main() {

    let csv_s = || csv::Reader::from_file("/Users/camdennarzt/Documents/All 7000 series-Table 1-1-1-3.csv").unwrap();
    let mut csv = csv_s();
    let mut index_data = io::Cursor::new(Vec::new());
    create_index(csv_s(), index_data.by_ref()).unwrap();
    let mut index = Indexed::open(csv_s(), index_data).unwrap();

    let mut tried_indices = Vec::new();
    let mut threads : Vec<_> = (0..num_cpus::get()).map(|i|{
        thread::spawn(move || {
            let mut best_set : Vec<Vec<String>> = Vec::new();
            let mut best_count = 0;
            let mut rng = thread_rng();
            let mut indices = Vec::new();
            let limit = 2usize.pow(10)/num_cpus::get();
            for _ in (0..limit) {
                while {
                    let count = *sample(&mut rng, 13..83, 1).first().unwrap();
                    indices = sample(&mut rng, 1..83, count);
                    tried_indices.contains(&indices)
                }{}
                tried_indices.push(indices.to_owned());

                let current_set:Vec<_> = indices.iter().map(|&i|{
                    index.seek(i).unwrap();
                    index.records().next().unwrap().unwrap()
                }).collect();
                let current_count = mets_in_common(&mut csv,current_set.len());
                if (current_count > best_count){
                    best_count = current_count;
                    best_set = current_set;
                }
            }
            (best_count,best_set.iter().map(|r| *r.first().unwrap()).collect::<Vec<String>>())
        })
    }).collect();
}

特别是当我编译它(生锈1.2稳定)时,我得到:

-*- mode: compilation; default-directory: "~/Developer/Rust/optimal_subset_finder/src/" -*-
Compilation started at Wed Aug 19 14:55:10

cargo build
   Compiling optimal_subset_finder v0.1.0 (file:///Users/camdennarzt/Developer/Rust/optimal_subset_finder)
main.rs:31:23: 56:10 error: cannot move out of captured outer variable in an `FnMut` closure
main.rs:31         thread::spawn(move || {
main.rs:32             let mut best_set : Vec<Vec<String>> = Vec::new();
main.rs:33             let mut best_count = 0;
main.rs:34             let mut rng = thread_rng();
main.rs:35             let mut indices = Vec::new();
main.rs:36             let limit = 2usize.pow(10)/num_cpus::get();
           ...
note: in expansion of closure expansion
main.rs:31:23: 56:10 note: expansion site
note: in expansion of closure expansion
main.rs:30:57: 57:6 note: expansion site
main.rs:31:23: 56:10 error: cannot move out of captured outer variable in an `FnMut` closure
main.rs:31         thread::spawn(move || {
main.rs:32             let mut best_set : Vec<Vec<String>> = Vec::new();
main.rs:33             let mut best_count = 0;
main.rs:34             let mut rng = thread_rng();
main.rs:35             let mut indices = Vec::new();
main.rs:36             let limit = 2usize.pow(10)/num_cpus::get();
           ...
note: in expansion of closure expansion
main.rs:31:23: 56:10 note: expansion site
note: in expansion of closure expansion
main.rs:30:57: 57:6 note: expansion site
main.rs:31:23: 56:10 error: cannot move out of captured outer variable in an `FnMut` closure
main.rs:31         thread::spawn(move || {
main.rs:32             let mut best_set : Vec<Vec<String>> = Vec::new();
main.rs:33             let mut best_count = 0;
main.rs:34             let mut rng = thread_rng();
main.rs:35             let mut indices = Vec::new();
main.rs:36             let limit = 2usize.pow(10)/num_cpus::get();
           ...
note: in expansion of closure expansion
main.rs:31:23: 56:10 note: expansion site
note: in expansion of closure expansion
main.rs:30:57: 57:6 note: expansion site
main.rs:55:49: 55:68 error: cannot move out of borrowed content
main.rs:55             (best_count,best_set.iter().map(|r| *r.first().unwrap()).collect::<Vec<String>>())
                                                           ^~~~~~~~~~~~~~~~~~~
note: in expansion of closure expansion
main.rs:55:45: 55:68 note: expansion site
note: in expansion of closure expansion
main.rs:31:23: 56:10 note: expansion site
note: in expansion of closure expansion
main.rs:30:57: 57:6 note: expansion site
error: aborting due to 4 previous errors
Could not compile `optimal_subset_finder`.

To learn more, run the command again with --verbose.

Compilation exited abnormally with code 101 at Wed Aug 19 14:55:10

如果我完全注释掉线程,那么它会编译并运行。但我无法解析有关使线程正常工作的文档。当涉及到知道如何解决这个问题时,错误并不是特别有用。

1 个答案:

答案 0 :(得分:5)

您的代码存在一些问题。

首先,您显示的实际错误是由于您尝试从File / Project Groups ...中获取String而导致的。如果没有克隆,这是不可能的,因为它需要将&Vec<String>移出String,但您无法移出引用的数据。由于您的&Vec<String>在内部线程中使用,并且在您从中返回数据后立即删除,因此您可以使用best_set安全地使用它:

into_iter()

然而,这是最少的问题。你在并发部分有很多错误。

首先,您尝试直接在多个线程中使用best_set.into_iter() .flat_map(|r| r.take(1)) .collect::<Vec<String>>() 。这是不可能的,因为index驻留在父线程堆栈中。父线程可以在其子线程之前终止(这确实是在程序编译时会发生的情况),并且在这种情况下会导致index被破坏,因此子线程将访问垃圾数据。要解决此问题,您需要使用Arc进行某种同步,例如Mutex。如果没有互斥锁,您可以在没有同步的情况下从多个线程中访问相同的数据,这是数据竞争的一个完美示例,会导致未定义的行为。

index

您需要对let index = Arc::new(Mutex::new(Indexed::open(csv_s(), index_data).unwrap())); ... let index = index.clone(); thread::spawn(move || { ... let current_set:Vec<_> = { let index = index.lock(); indices.iter().map(|&i| { index.seek(i).unwrap(); index.records().next().unwrap().unwrap() }).collect() }; ... }); 执行相同的操作 - 将数据从多个线程推送到同一个向量中,因此您需要某种同步才能安全地执行此操作。你应该小心范围,以免锁定时间超过必要的时间 - 记住,当tried_indices方法返回的警卫超出范围时,互斥锁被释放。

我能看到的最后一个问题,也就是最严重的一个问题,就是你在所有衍生线程中使用相同的lock()。虽然这不仅因为可变访问而导致数据争用,但它也是错误的,因为读者根据定义是一个可耗尽的数据源。如果你从多个线程中读取它,即使不考虑并发问题,你也可以从它读取的任何部分获得绝对随机的数据。因此,即使将读者置于互斥锁中也无法解决问题。

我认为最简单的解决方案是为每个线程创建一个单独的阅读器。幸运的是,您已经有了创建读者的功能,所以只需在产生线程之前使用它:

csv

最后,您似乎没有使用线程返回的计算结果,但您可能已经知道了。