素数探し
とある正整数nがあって、2以上n未満のいかなる数もnを割り切ることができないとき、nは素数です。このルールに忠実に従うなら、nが素数であるか否かを判定する関数: is_prime()は以下のように定義できます。
bool is_prime(unsigned int n) { if ( n < 2U ) return false; // 0と1は素数じゃない // 2以上n未満の数 i が... for ( unsigned int i = 2U; i < n; ++i ) { // nを割り切るなら、nは素数じゃない if ( n % i == 0 ) return false; } return true; }
コレを使って5より大きな素数をすべて見つけるコードは:
// 2,3,5で割れない数は30の倍数+1,7,11... unsigned int nth_prime_candidate(unsigned int x, unsigned int y) { static std::array<unsigned int,8> bias = { 1, 7, 11, 13, 17, 19, 23, 29 }; return 30*x + bias[y]; } inline unsigned int nth_prime_candidate(unsigned int n) { return nth_prime_candidate(n/8, n%8); } // 素数探し:見つけた素数はprimesに追加する void prime_sequential(unsigned int limit, std::vector<unsigned int>& primes) { for (unsigned int i = 0; i < limit; ++i) { unsigned int n = nth_prime_candidate(i); if ( is_prime(n) ) primes.push_back(n); } }
処理時間を計測し、表示しましょうか。
inline std::chrono::milliseconds measure_exec(const std::function<void(unsigned int, std::vector<unsigned int>&)>& fun, unsigned int limit, std::vector<unsigned int>& result) { std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now(); fun(limit, result); return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start); } int main() { const unsigned int LIMIT = 40000; vector<unsigned int> primes; vector<unsigned int>::size_type count; std::chrono::milliseconds durration; primes.clear(); durration = measure_exec(&prime_sequential, LIMIT, primes); count = primes.size(); cout << count << " primes found." << endl; cout << "----- sequential" << endl; cout << durration.count() << "[ms]" << endl; }
僕の愛機(CPU:i7-2600K)では、13845個の素数を見つけるのに5799[ms]かかりました。
PPLの並列アルゴリズム
PPLでは"同時に(互いに干渉せずに)実行できる処理単位"を、タスク(task)と称しています。PPLの中ではCPUの力量に見合う数のスレッドを起こし、それらスレッドにユーザが定義した(複数の)タスクを投げ込んで処理を行います。そのからくりのおかげで、ユーザは明示的にスレッドを管理する必要がありません。加えて今回紹介する"parallel_"から始まる各種並列アルゴリズムはタスクの管理もユーザには任せず、「与えられた複数の処理を並列に実行し、全処理が完了するまで待つ(fork-join)」一連のパターンが関数内に封じ込められています。
それでは、PPLが提供する並列アルゴリズムを1つずつ紹介しつつ、素数探しを高速化してみましょうか。