web-dev-qa-db-ja.com

C ++の並列ループ

C++で並列にforループやforループなどのループを計算するための軽量で簡単な方法があるのだろうか。そのようなことをどのように実装しますか? From Scalaマップ、フィルター、およびforeach関数を知っています。これらの並列処理を実行することもできますか?C++でこれを実現する簡単な方法はありますか?私の主なプラットフォームはLinuxですがクロスプラットフォームで機能する場合は便利です。

27
Exagon

C++ 17の並列アルゴリズムを使用すると、次を使用できます。

std::vector<std::string> foo;
std::for_each(
    std::execution::par_unseq,
    foo.begin(),
    foo.end(),
    [](auto&& item)
    {
        //do stuff with item
    });

並列にループを計算します。最初のパラメーターは 実行ポリシー を指定します

29
Exagon

プラットフォームは何ですか? OpenMPを見ることができますが、C++の一部ではありません。ただし、コンパイラによって広くサポートされています。

範囲ベースのforループについては、例えば、 C++ 11範囲ベースのforループでのOpenMPの使用? を参照してください。

また、 http://www.open-std.org で、将来のC++に並列構造/アルゴリズムを組み込むための努力を示すドキュメントはほとんど見ませんでしたが、現在の状態はわかりません。

[〜#〜] update [〜#〜]

いくつかの模範的なコードを追加するだけです:

template <typename RAIter>
void loop_in_parallel(RAIter first, RAIter last) {
   const size_t n = std::distance(first, last);

   #pragma omp parallel for
   for (size_t i = 0; i < n; i++) {
       auto& elem = *(first + i);
       // do whatever you want with elem
    }
}

スレッドの数は、OMP_NUM_THREADS環境変数を使用して実行時に設定できます。

19
Daniel Langr

std::async は、C++ランタイムが並列処理を制御します。

Cppreference.comの例:

#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <future>

template <typename RAIter>
int parallel_sum(RAIter beg, RAIter end)
{
    auto len = end - beg;
    if(len < 1000)
        return std::accumulate(beg, end, 0);

    RAIter mid = beg + len/2;
    auto handle = std::async(std::launch::async,
                              parallel_sum<RAIter>, mid, end);
    int sum = parallel_sum(beg, mid);
    return sum + handle.get();
}

int main()
{
    std::vector<int> v(10000, 1);
    std::cout << "The sum is " << parallel_sum(v.begin(), v.end()) << '\n';
}
12
bobah

C++ 11では、わずか数行のコードでforループを並列化できます。これにより、forループが小さなチャンクに分割され、各サブループがスレッドに割り当てられます。

/// Basically replacing:
void sequential_for(){
    for(int i = 0; i < nb_elements; ++i)
        computation(i);
}

/// By:
void threaded_for(){
    parallel_for(nb_elements, [&](int start, int end){ 
        for(int i = start; i < end; ++i)
            computation(i); 
    } );
}

または、クラスのウィッシング:

struct My_obj {

    /// Replacing:
    void sequential_for(){
        for(int i = 0; i < nb_elements; ++i)
            computation(i);
    }

    /// By:
    void threaded_for(){
        parallel_for(nb_elements, [this](int s, int e){ this->process_chunk(s, e); } );
    }

    void process_chunk(int start, int end)
    {
        for(int i = start; i < end; ++i)
            computation(i);
    }
};

これを行うには、以下のコードをヘッダーファイルに入れて自由に使用するだけです。

#include <algorithm>
#include <thread>
#include <functional>
#include <vector>

/// @param[in] nb_elements : size of your for loop
/// @param[in] functor(start, end) :
/// your function processing a sub chunk of the for loop.
/// "start" is the first index to process (included) until the index "end"
/// (excluded)
/// @code
///     for(int i = start; i < end; ++i)
///         computation(i);
/// @endcode
/// @param use_threads : enable / disable threads.
///
///
static
void parallel_for(unsigned nb_elements,
                  std::function<void (int start, int end)> functor,
                  bool use_threads = true)
{
    // -------
    unsigned nb_threads_hint = std::thread::hardware_concurrency();
    unsigned nb_threads = nb_threads_hint == 0 ? 8 : (nb_threads_hint);

    unsigned batch_size = nb_elements / nb_threads;
    unsigned batch_remainder = nb_elements % nb_threads;

    std::vector< std::thread > my_threads(nb_threads);

    if( use_threads )
    {
        // Multithread execution
        for(unsigned i = 0; i < nb_threads; ++i)
        {
            int start = i * batch_size;
            my_threads[i] = std::thread(functor, start, start+batch_size);
        }
    }
    else
    {
        // Single thread execution (for easy debugging)
        for(unsigned i = 0; i < nb_threads; ++i){
            int start = i * batch_size;
            functor( start, start+batch_size );
        }
    }

    // Deform the elements left
    int start = nb_threads * batch_size;
    functor( start, start+batch_remainder);

    // Wait for the other thread to finish their task
    if( use_threads )
        std::for_each(my_threads.begin(), my_threads.end(), std::mem_fn(&std::thread::join));
}

最後に、さらにコンパクトな式を取得するマクロを定義できます。

#define PARALLEL_FOR_BEGIN(nb_elements) tbx::parallel_for(nb_elements, [&](int start, int end){ for(int i = start; i < end; ++i)
#define PARALLEL_FOR_END()})

TBX_PARALLEL_FOR_BEGIN(nb_edges)
{
    computation(i);
}TBX_PARALLEL_FOR_END();
9
arkan

これは、threads特にpthreadsライブラリ関数を使用して実行できます。この関数は、操作を同時に実行するために使用できます。

詳細については、こちらをご覧ください: http://www.tutorialspoint.com/cplusplus/cpp_multithreading.htm

std :: threadも使用できます: http://www.cplusplus.com/reference/thread/thread/

以下は、各スレッドのスレッドIDを使用して配列を2つの半分に分割するコードです:

#include <iostream>
#include <cstdlib>
#include <pthread.h>

using namespace std;

#define NUM_THREADS 2

int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

void *splitLoop(void *threadid)
{
   long tid;
   tid = (long)threadid;
   //cout << "Hello World! Thread ID, " << tid << endl;
   int start = (tid * 5);
   int end = start + 5;
   for(int i = start;i < end;i++){
      cout << arr[i] << " ";
   }
   cout << endl;
   pthread_exit(NULL);
}

int main ()
{
   pthread_t threads[NUM_THREADS];
   int rc;
   int i;
   for( i=0; i < NUM_THREADS; i++ ){
      cout << "main() : creating thread, " << i << endl;
      rc = pthread_create(&threads[i], NULL, 
                          splitLoop, (void *)i);
      if (rc){
         cout << "Error:unable to create thread," << rc << endl;
         exit(-1);
      }
   }
   pthread_exit(NULL);
}

また、コンパイル時に-lpthreadフラグを使用する必要があることを忘れないでください。

Ideoneのソリューションへのリンク: http://ideone.com/KcsW4P

4
uSeemSurprised

Concurrency :: parallel_for(PPL)は、タスクの並列化を行うための素晴らしいオプションの1つでもあります。

C++コーディング演習– Parallel For –モンテカルロPI計算

int main() {
    srand(time(NULL)); // seed
    const int N1 = 1000;
    const int N2 = 100000;
    int n = 0;
    int c = 0;
    Concurrency::critical_section cs;
    // it is better that N2 >> N1 for better performance
    Concurrency::parallel_for(0, N1, [&](int i) {
        int t = monte_carlo_count_pi(N2);
        cs.lock(); // race condition
        n += N2;   // total sampling points
        c += t;    // points fall in the circle
        cs.unlock();
    });
    cout < < "pi ~= " << setprecision(9) << (double)c / n * 4.0 << endl;
    return 0;
}
1
justyy