Home > Archives > 2007年11月27日

2007年11月27日

libstdc++ parallel mode を試す

EuroPar2007で発表のあった The Multi-Core Standard Template Library (MCSTL) が GCC に統合されている(されつつある?)らしいので,セミナーのネタにと試用してみた.GCCのオフィシャルページを見ると libstdc++ parallel mode という名前で統合中らしい.こいつは既存の STL を OpenMP による並列実装の STL に置換しましょうというもので,既存の STL で書かれたコードがそのままで並列プログラムになってくれる.

んで,試そうと思ったら最新のGCCリリースであるところの 4.2.2 にも入っていないということに GCC4.2.2 のビルドが終わった時点で気づき… しょうがないので svn のリポジトリから最新のソースを落としてきて GCC をビルドした(8コアくらいあるマシンで make -j8 とかやるとビルドがとても速い).とりあえずソースの状態で libstdc++-v3/include/parallel というディレクトリがあれば parallel mode が使える.

準備できたので次の std::sort を使ったプログラムを試してみた.

#include <iostream>
#include <algorithm>
#include <vector>
const int count = 1000000, num_tests = 10;
int main(int argc, char **argv)
{
    std::vector<int> v(count);
    for (int i = 0; i < num_tests; i++)
    {
        std::generate(v.begin(), v.end(), rand);
        std::sort(v.begin(), v.end());
        std::cout << "." << std::flush;
    }
}

コンパイルは parallel mode 用のフラグ(-D_GLIBCXX_PARALLEL -fopenmp)を立てればよいらしい.

 g++-XXX test.cpp -o testS -march=native  # sequential mode
 g++-XXX test.cpp -o testP -march=native -D_GLIBCXX_PARALLEL -fopenmp  # parallel mode

Quad の Xeon x2 という8コアで動かした結果は以下のとおり.

> OMP_NUM_THREADS=8 time ./testS   # sequential mode
..........        6.26 real         6.24 user         0.01 sys
> OMP_NUM_THREADS=8 time ./testP   # parallel mode
..........        1.39 real         5.66 user         0.75 sys

普通に4~5倍速くなってるのを確認.すげー.並列プログラミング簡単.とりあえず sort は簡単に並列に動いてくれた.

ついでに, parallel mode は他にも並列実装を持っているらしいので試してみた.

まず std::partial_sum.4.51秒が1.65秒になった.8コアで約四倍.

#include <iostream>
#include <algorithm>
#include <vector>
#include <numeric>
struct one {
  int operator()() const {
    return 1;
  }
};
const int count = 10000000, num_tests = 10;
int main(int argc, char **argv)
{
    std::vector<int> v(count);
    std::vector<int> r(count);
    std::generate(v.begin(), v.end(), one());
    for (int i = 0; i < num_tests; i++)
    {
        std::partial_sum(v.begin(), v.end(), r.begin());
        std::cout << "." << std::flush;
    }
}

次は std::accumulate.3.20秒が3.79秒に増えた.全要素の和をとるだけなので普通に考えると8コアで8倍いきそうだけど… 速くならない理由は不明.

#include <iostream>
#include <algorithm>
#include <vector>
#include <numeric>
struct one {
  int operator()() const {
    return 1;
  }
};
const int count = 10000000, num_tests = 10;
int main(int argc, char **argv)
{
    std::vector<int> v(count);
    std::generate(v.begin(), v.end(), one());
    for (int i = 0; i < num_tests; i++)
    {
        int sum = std::accumulate(v.begin(), v.end(), 0);
        std::cout << "." << std::flush;
    }
}

最後に試したのは std::for_each.3.81秒が3.75秒.全要素に独立に関数適用するだけなので8コアで8倍いきそうだけど… 速くならない理由は不明.

#include <iostream>
#include <algorithm>
#include <vector>
#include <numeric>
struct f {
  void operator()(int &x) const {
    x = x + 1;
  }
};
struct one {
  int operator()() const {
    return 1;
  }
};
const int count = 10000000, num_tests = 10;
int main(int argc, char **argv)
{
    std::vector<int> v(count);
    std::generate(v.begin(), v.end(), one());
    for (int i = 0; i < num_tests; i++)
    {
        std::for_each(v.begin(), v.end(), f());
        std::cout << "." << std::flush;
    }
}

ということで,簡単に並列化できそうな部分でいろいろと失敗した模様.使い方が悪いのか実装途中なのか? よくわからんけどうまく動いた部分に関しては効果が確認できたのでよし.

Home > Archives > 2007年11月27日

Search
Feeds

Page Top