std::asyncによる並列for_each

C++ではOpenMPよりも、std algorithmが並列に動いた方が便利だよね。ということで作りました。元ネタはhttps://twitter.com/cpp_akira/status/16195866927です。

#ifdef _OPENMP
inline size_t concurrency_omp() {
    size_t n;
#pragma omp parallel
    { n = omp_get_num_threads(); }
    return n;
}
#endif

inline size_t concurrency() {
#ifdef _OPENMP
    static size_t const n{concurrency_omp()};
#else
    // g++ 4.5's hardware_concurrency() always returns zero :-(
    auto const n = std::thread::hardware_concurrency();
#endif
    return std::max<decltype(n)>(1, n);
}

template <class It, class F>
void for_each(It begin, It end, F&& f) {
    auto const total = std::distance(begin, end);
    auto const c = (size_t)total < concurrency() ? 1 : concurrency();
    auto const num = total / c;

    std::vector<std::future<void> > futures;
    futures.reserve(c);

    for (size_t i = 0; i < c; ++i) {
        futures.push_back(std::async([&, i] {
            std::for_each(begin + num * i, begin + num * (i + 1), f);
        }));
    }
    std::for_each(begin + num * c, end, f);
    std::for_each(futures.begin(), futures.end(), [] (std::future<void>& f) { f.wait(); });
}

割と適当です。Itはrandom access iteratorでないとエラーになります*1。g++のstd::thread::hardware_concurrencyは常に0が帰ってくるので、OpenMPが使える時はそっちで並列度を取ってます。static変数の初期化はMT-safeなので、concurrencyを同時に呼んでも大丈夫だよね…?

Mac環境だとのサポートがまだないので、結構不便です…。

*1:std::vector::iterator とか