4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

C++の並列ソート

Last updated at Posted at 2020-05-06

https://qiita.com/dc1394/items/d2b8de600a304664cdb5 を読んで、 __gnu_parallel::sort が無いなと思って記事を書くことにした。

整列の仕方

整列アルゴリズムにはいろいろあるけど、ライブラリ提供者が普通これが速いんじゃないの、と思うものが、不安定ソートとしては std::sort という形で、安定ソートとしては std::stable_sort という形で提供されている。

でもそれは並列計算ができない場合の話。
並列計算ができる場合は、それを活かせばもっと速くなる。かもしれない。そして、今どきのコンピュータはだいたい並列計算できるようなハードウェアになっている。

並列計算を使った整列を実現する方法はいくつかあるんだけど、手軽なのは、 gcc 4.6.0 のころから存在していたらしい __gnu_parallel::sort 。というか、これしか知らなかった。

で。
この記事を書こうと調べていたら、C++17 で並列計算が標準でサポートされるようになったことを知った。

c++17
std::sort(first, last, comp);

などを、 #include <execution> とした上で、

c++17
std::sort(std::execution::par, first, last, comp);

などとすると並列計算になるらしい。すばらしい。 par の部分は下表の通り4通り。

ポリシー マルチスレッド ベクトル化 C++
seq 禁止 禁止 C++17以降
par 許可 禁止 C++17以降
par_unseq 許可 許可 C++17以降
unseq 禁止 許可 C++20以降

上記以外も処理系の気持ち次第で増えるかもしれないらしい。

処理時間を短くしたいならとりあえず par_unseq を指定しておけばいいかなという感じ。

とはいえ。手元の環境ではただ単に上記のように書けばいいわけではなく

$ brew install tbb

を実行しておく必要がある。リンクするライブラリとかも設定が必要。
TBB は、 Intel Threading Building Blocks のことらしい。

実行結果

安定ソート、不安定ソートのそれぞれについて。
以下の5つの方法で、4種類のデータを整列した。データの件数は $2^{10}$〜$2^{20}$ とした。

整列方法

名前 実装(xxx は、sortstable_sort)
gnu_parallel __gnu_parallel::xxx(b, e)
std std::xxx(b, e)
std(par) std::xxx(略::par, b, e)
std(par_unseq) std::xxx(略::par_unseq, b, e)
std(unseq) std::xxx(略::unseq, b, e)

unseq は C++20 以降のハズなんだけど、 -std=c++17 でも使えた。

データ

名前 意味
random_uint32 $2^{32}$種類の値を持つ、ランダムな std::uint32_t
few_uint32<8> 8種類の値しか取れない、ランダムな std::uint32_t
random_string<1024> すべての文字がランダムな英小文字の std::string 。長さは 1024文字。
few_strings<8, 1024> 8種類の値しか取れない、ランダムな std::string 。長さは 1024文字。

整列済みとかほぼ整列済みとか逆順とかいろいろ試そうかとも思ったけど、控えめにしてみた。

結果

軸にラベル付け忘れた。
縦軸はミリ秒。整列1回に要する時間。
横軸はデータの件数。

マシンは MacBook Pro (Retina, 15-inch, Mid 2015)。クアッドコアIntel Core i7。

コンパイラは g++-9 (Homebrew GCC 9.3.0) 9.3.0
コンパイルオプションは -march=native -O2 にした。

不安定ソート

image.png

意外と gnu_parallel がふるわない。
uint32_t で件数が多いと gnu_parallel がやや速い。
それ以外では gnu_parallel は意外に遅い。特に、 uint32_t で件数が少ないと顕著。

std(par) と std(par_unseq) が速い。同じかなとも思う。
std(unseq) は std と同じかな。

件数が少ないと並列計算のメリットは少ない。特に gnu_parallel はかえって遅い。

安定ソート

image.png

不安定ソートと傾向は同じ。

まとめ

  • 試した範囲では std::execution::parstd::execution::par_unseq を使ったものが速い。
  • 件数が少ないと並列計算のメリットは少ない。
  • g++-10 で試したほうが良かった?

補足

ソースコードなどを https://github.com/nabetani/parallelsort においている。

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?