Rust并行优势
Rayon 的高级并行性
Rayon 提供了多个并行迭代器适配器,可用于并行化迭代器上的常见操作,例如 、 和 。让我们看一些例子。
Parallel Map
考虑以下示例,我们要计算向量中每个元素的平方:
fn main() {
let numbers = vec![1, 2, 3, 4, 5];
let squares: Vec<_> = numbers.iter().map(|x| x * x).collect();
println!("{:?}", squares);
}
要使用 Rayon 并行化此代码,只需使用以下方法将标准迭代器替换为并行迭代器:
use rayon::prelude::*;
fn main() {
let numbers = vec![1, 2, 3, 4, 5];
let squares: Vec<_> = numbers.par_iter().map(|x| x * x).collect();
println!("{:?}", squares);
}
Parallel Filter
集合中的过滤元素也可以与 Rayon 并行。考虑以下示例,我们要从向量中提取偶数:
fn main() {
let numbers = vec![1, 2, 3, 4, 5];
let even_numbers: Vec<_> = numbers.iter().filter(|x| x % 2 == 0).collect();
println!("{:?}", even_numbers);
}
要使用 Rayon 并行化此代码,请将标准迭代器替换为并行迭代器:
use rayon::prelude::*;
fn main() {
let numbers = vec![1, 2, 3, 4, 5];
let even_numbers: Vec<_> = numbers.par_iter().filter(|x| x % 2 == 0).collect();
println!("{:?}", even_numbers);
}
Parallel Reduce
将集合减少为单个值是另一个可以从并行性中受益的常见操作。下面是计算向量中元素之和的示例:
fn main() {
let numbers = vec![1, 2, 3, 4, 5];
let sum: i32 = numbers.iter().cloned().reduce(|a, b| a + b).unwrap();
println!("Sum: {}", sum);
}
要使用 Rayon 并行化此代码,请将标准迭代器替换为并行迭代器并使用“reduce”方法:
use rayon::prelude::*;
fn main() {
let numbers = vec![1, 2, 3, 4, 5];
let sum: i32 = numbers.par_iter().cloned().reduce(|| 0, |a, b| a + b);
println!("Sum: {}", sum);
}
Rayon 的高级并行性
自定义并行算法
Rayon 允许您使用自定义函数创建自定义并行算法。该函数创建一个新的并行范围,同时该函数允许您在该范围内创建新的并行任务。这些任务可以共享对数据的引用,从而更轻松地处理复杂的数据结构。
以下是计算两个向量之和的简单自定义并行算法的示例:
use rayon::prelude::*;
fn main() {
let a = vec![1, 2, 3, 4, 5];
let b = vec![6, 7, 8, 9, 10];
let result: Vec<_> = (0..a.len()).into_iter().collect();
let result = &result;
rayon::scope(|scope| {
for (i, (ai, bi)) in a.iter().zip(b.iter()).enumerate() {
let result = &result[i];
scope.spawn(move |_| {
*result.lock().unwrap() = ai + bi;
});
}
});
println!("Result: {:?}", result);
}
在此示例中,我们使用创建一个新的并行作用域。在范围内,我们迭代输入向量 和 的索引和元素。对于每对元素,我们生成一个新的并行任务,计算两个元素的总和并将结果存储在向量的相应位置。
Parallel Sort
Rayon 提供了排序算法的并行实现,可以显着加快对大量数据集合的排序速度。要使用并行排序,只需在可变切片上调用该方法即可:
use rayon::prelude::*;
fn main() {
let mut numbers = vec![5, 4, 3, 2, 1];
numbers.par_sort();
println!("{:?}", numbers);
}
您还可以使用 和 方法来自定义排序行为,就像使用标准 和 方法一样。
并行连接
Rayon 的join
函数允许您并行执行两个闭包并等待它们都完成。这对于将任务划分为两个可以同时执行的独立子任务非常有用。
下面是并行计算斐波那契数列的示例:
use rayon::prelude::*;
fn parallel_fibonacci(n: u32) -> u32 {
if n <= 2 {
1
} else {
let (a, b) = rayon::join(|| parallel_fibonacci(n - 1), || parallel_fibonacci(n - 2));
a + b
}
}
fn main() {
let n = 20;
let fib = parallel_fibonacci(n);
println!("Fibonacci({}) = {}", n, fib);
}
在此示例中,我们使用 rayon::join
并行计算斐波那契数。当F(n-1)和F(n-2)
两个计算完成后,我们将结果相加以获得F(n)
的最终值。
Rayon 中的错误处理
错误处理是编写并发代码的一个重要方面。 Rust 的类型用于表示可能失败的计算结果。 Rayon 提供了一种在并行迭代器中处理类型的方法。
考虑以下示例,我们要计算向量中每个元素的平方根并处理潜在的错误:
use rayon::prelude::*;
fn safe_sqrt(x: f64) -> Result<f64, String> {
if x = 0.0 {
Ok(x.sqrt())
} else {
Err(format!("Cannot compute the square root of a negative number: {}", x))
}
}
fn main() {
let numbers = vec![1.0, 2.0, 3.0, -1.0, 4.0];
let results: Result<Vec<_>, _> = numbers
.par_iter()
.map(|x| safe_sqrt(*x))
.collect::<Vec<_>>()
.into_iter()
.collect();
println!("{:?}", results);
}
在此示例中,我们使用该函数计算向量中每个元素的平方根。该函数返回一个类型,指示计算是否成功或发生错误。
我们首先使用该函数将每个数字映射到其平方根。然后,我们将结果收集到一个向量中,并将值向量转换为包含值向量的单个向量。这样,我们就可以处理并行计算期间发生的任何错误。