Help us understand the problem. What is going on with this article?

Rust:rayonでお手軽・安全に並列処理

More than 1 year has passed since last update.

この記事はWanoアドベントカレンダーの10日目の記事です。

今回も何番煎じかな?みたいな内容でお送りいたします。

なお、Rust v1.31.0でRust 2018がリリースされましたので、前回辺りからRust 2018でコードを書いています。
ただRust 2018と言ってもextern crateがサンプルコードから消えた程度の違いしかありません。

TL;DR

  • rayonParallelIteratorstd::iter::Iteratorと同様のmapfilterfold等があり、これらが自動で並列処理される。
  • iterメソッドと同様にpar_iterメソッドを呼ぶとParallelIteratorが取れる。
  • par_sortってメソッドでソートも並列化してくれる。意外と並列化のオーバーヘッドが少なくて速い。
  • rayonの詳細はこのブログ記事を参照

rayonとは

他の言語とかであったりするループの各イテレーションを並列化できるライブラリと同じような事が出来るライブラリです。
ただしループ処理ではなくイテレーターを並列化します。

rayonでコードを書いてみましょう

map

たとえば配列の値に応じたフィボナッチ数のうち最大の物をナイーブに求める場合はこんな感じになります。

use rand::prelude::*;
use rayon::prelude::*;
use std::time::Instant;

fn fib(i: u64) -> u64 {
    match i {
        0 => 1,
        1 => 1,
        _ => fib(i-2) + fib(i-1),
    }
}

fn main() {
    let mut rng = thread_rng();
    let xs: Vec<_> = (0..100000).map(|_| rng.gen_range(0, 24)).collect();
    let ys = xs.clone();

    let begin = Instant::now();
    let max = xs.par_iter().map(|&x| fib(x)).max(); // `par_iter`を使って並列化
    println!("Parallel: {:?}; elapsed: {:?}", max, begin.elapsed());

    let begin = Instant::now();
    let max = ys.iter().map(|&x| fib(x)).max(); // 普通のIterator
    println!("Sync: {:?}; elapsed: {:?}", max, begin.elapsed());
}

use rayon::prelude::*で必要なtraitとか型とかが全て使えるようになります。
あとは par_iterでParallelIteratorを取得したら、おなじみのmapとかを呼ぶだけです。

上のコードを動かしてみたら結果は次の様になりました。

Parallel: Some(46368); elapsed: 525.928662ms
Sync: Some(46368); elapsed: 2.659804912s

rayonを用いた方が5倍くらい速いですが、これは実行した環境が5コアのVMだからでしょう。
rayonは論理CPUコア数と同じ数のスレッドを用いて処理しますので、このコードの様に1イテレーションの処理が重くて他の処理に依存していない場合はいい感じにスケールします。

collect

処理結果をcollectメソッドでVecにする事もできます。

use rayon::prelude::*;

fn main() {
    let xs = vec![1, 2, 3, 4, 5];
    let ys: Vec<_> = xs.par_iter().map(|&x| x * x).collect();
    println!("xs: {:?}", xs);
    println!("ys: {:?}", ys);
}

出力:

xs: [1, 2, 3, 4, 5]
ys: [1, 4, 9, 16, 25]

お手軽ですね。

par_sort

par_sortメソッドを使うとソートが出来るのでやってみましょう。

use rand::prelude::*;
use rayon::prelude::*;
use std::time::Instant;

fn main() {
    let mut xs: Vec<i32> = (0..1000000).map(|_| random()).collect();
    let mut ys = xs.clone();
    let i = random::<usize>() % 1000000;

    let begin = Instant::now();
    xs.par_sort();
    println!("[Parallel] xs[{}]: {}; elapsed: {:?}", i, xs[i], begin.elapsed());

    let begin = Instant::now();
    ys.sort();
    println!("[Sync] ys[{}]: {}; elapsed: {:?}", i, ys[i], begin.elapsed());
}

結果:

[Parallel] xs[376272]: -527567671; elapsed: 40.352851ms
[Sync] ys[376272]: -527567671; elapsed: 60.221255ms

CPUが5コアのVMで100万要素の配列に対して実行して、rayonを用いた方がいくらか速いという感じになりました。
ただ、配列の要素数がもっと少なかったり、CPUコア数がもっと少ない場合にはrayonを用いた方がオーバーヘッドの分で遅くなる場合もあり得ます(これは他の処理でも言えます)。

終わりに

様々な事情で2コアのVMとかを使っている場合はあまり恩恵が無いかもしれませんが、多コア環境で実行するコードを書くような時は必要に応じて使用を検討してみると良いかもしれません。
少なくとも自前で頑張るよりは圧倒的に良いと思います。

rayon自体がどう動いているのか気になる方は下記のブログ記事で解説されているのでそちらをご覧ください(余裕があったら後で要約を書き足したいと思います)。
http://smallcultfollowing.com/babysteps/blog/2015/12/18/rayon-data-parallelism-in-rust/

fujitayy
wano-inc
「Cultivate your dream」をミッションにクリエイター・アーティストを支援する事業やサービスを展開
https://wano.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした