2016-03-27 2 views
9

Интересно, есть ли простой и простой способ вычисления циклов, например, для диапазонов, основанных на циклах, параллельных в C++. Как бы вы реализовали такую ​​вещь? Из Scala я знаю, что функции map, filter и foreach, возможно, также могут выполнять эти параллели? Есть ли простой способ достичь этого на C++. Моей первичной платформой является Linux, но было бы неплохо, если бы она работала с кросс-plattform.Параллельные петли в C++

+0

Использование потоков - хороший вариант. –

+0

Не правда ли, дорого ли инициализировать потоки? – Exagon

+1

По сравнению с вызовом fork(). Нитки не дорогие, поскольку они делят ресурсы из основного потока, за исключением того, что у них есть собственный компьютер, регистры и стек. –

ответ

8

Что ваша платформа? Вы можете посмотреть OpenMP, хотя он не является частью C++. Но он широко поддерживается компиляторами.

Что касается диапазонов для петель, см., Например, Using OpenMP with C++11 range-based for loops?.

Я также видел несколько документов в http://www.open-std.org, которые указывают на некоторые попытки включить параллельные конструкции/алгоритмы в будущий C++, но не знают, каков их текущий статус.

ОБНОВЛЕНИЕ

Просто добавив некоторые иллюстративный код:

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 среды.

+0

Допустим, у меня есть не так дорогостоящая операция в цикле, можно ли разбить петлю пополам? На нитке одна половина другого остальное? То же самое с 3 и так далее потоками? – Exagon

+0

Что вы итерируете? Можете ли вы использовать индексы для циклирования? –

+0

@Exagon От вас зависит, что вы принимаете работу из потоков. Вы можете создавать условия в циклах, с помощью которых вы можете разделить работу. –

3

Это можно сделать с помощью threads, а именно pthreads библиотечной функции, которая может использоваться для одновременного выполнения операций.

Вы можете прочитать о них здесь: http://www.tutorialspoint.com/cplusplus/cpp_multithreading.htm

станд :: нить также может быть использован: http://www.cplusplus.com/reference/thread/thread/

Ниже приведен код, в котором я использовать идентификатор потока для каждого потока, чтобы разбить массив на две половинки:

#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

+1

Да, что может быть достигнуто, вы можете указать «начальный индекс» и «конец индекса» в функции, которую вы используете, и соответствующим образом изменить ее для каждого используемого потока. – uSeemSurprised

+1

Функция 'pthread_create' принимает в стороне аргумент, который содержит имя функции, которую вы хотите использовать ваши потоки, вы можете изменить эти аргументы функций для достижения желаемого результата. – uSeemSurprised

+3

Почему он хочет использовать pthreads, когда у него есть 'std :: thread'? –

8

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'; 
} 
+0

wow thanks Я думаю, что буду использовать это – Exagon

+0

Есть ли какая-либо документация о том, как C++ обрабатывает параллельные задачи и асинхронные? – Exagon

+1

Первый параметр 'std :: async()' указывает, какую свободу вы предоставляете структуре (в первую очередь, разрешаете ли вы использовать поток переднего плана). Что касается того, что он делает для фона - он специфичен для компилятора, но, скорее всего, на большинстве компиляторов будет один пул потоков с N = количеством ядер процессора на коробке. Лучшая документация по использованию, к которой я пришел, до сих пор является частью параллелизма из последней книги Майера. – bobah

5

С параллельных алгоритмов в C++ 17 теперь мы можем использовать:

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

вычислить петли параллельно. Первый параметр указывает execution policy

+0

Действительно ли какие-либо компиляторы действительно поддерживают это прямо сейчас? – nitronoid

+0

Компилятор Intel C++, если вы студент, вы можете получить его бесплатно. – Kaznov

1

С C++ 11 вы можете распараллелить цикл 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)); 
} 
Смежные вопросы