2016-10-27 2 views
1

У меня довольно большой вектор. Некоторые из элементов вектора параллельны определенному условию. Я хотел бы найти первый элемент, соответствующий условию.find_first of a vector in parallel in C++

Моя проблема очень похожа на этот вопрос (tbb: parallel find first element), но у меня нет tbb. Условие проверки очень утомительно (поэтому я не могу сделать это для всех из них последовательно). Вот почему я хотел бы запустить его параллельно. Я должен упомянуть, что мне хотелось бы найти первый элемент (поэтому для меня важна позиция индекса элемента).

Для примера, если у меня есть 4 потока.

ThreadNr Index  condition 
1   0   Not Meet 
2   1   Not Meet 
3   2   Not Meet 
4   3   Not Meet 

ThreadNr Index  condition 
1   4   Not Meet 
2   5   Meet 
3   6   Not Meet 
4   7   Meet 

Функция должна Retun порядковый номер 5. Нити должны быть распределены и работать на последовательном блоке итерации (размер блока может быть больше, что 1. Например, нить 1 работает на первых 4-х элементов, резьбы 2 на вторых 4 элементах и ​​т. Д.).

Для примера выше, если номер потока 4 (в индексе 7) нашел член до номера нити номер 2 (в индексе 5), он должен подождать, пока вся нить завершит работу. Как я уже говорил, самым низким номером индекса является цель.

Пожалуйста, исправьте меня, если у вас есть лучший алгоритм.

ПРИМЕЧАНИЕ: Я могу использовать внешние библиотеки, такие как прирост 1,62, OpenMP 2.0

+0

Обратите внимание, что наилучшая степень сложности возрастает от 0 до N/K, где N - общее количество элементов, а K - количество потоков. – rubenvb

+0

Это зависит от размера элементов и от того, как долго выполняется условие проверки. Если вы делаете что-то простое, например '==' против 'std :: vector ', вы не хотели бы чередоваться таким образом, потому что это может привести к хаосу в кеше. В этом случае вам понадобятся потоки, проверяющие блоки целых чисел за раз. Если ваши векторные объекты составляют 1 мб, и для выполнения проверки требуется 1-2 секунды, это чередование может иметь больше смысла. – RyanP

+0

Если tbb обращается к вам, используйте его. В Openmp, если я понимаю вопрос, вы бы разделили данные, как вы, кажется, сделали с внутренним циклом поиска, объединив внутренние результаты во внешнем сокращении omp firstprivate lastprivate или критически. – tim18

ответ

2

Поскольку OpenMP 2.0 не имеет конструкций отмены, вы должны реализовать его самостоятельно, например, с помощью общей переменной. Это также означает, что вы не можете использовать конструкцию обрезки for, так как выключение параллельных циклов не разрешено (поэтому OpenMP 4.0 ввел конструкции отмены). Если вы выполняете проверки отмены между оценкой каждого элемента, может случиться, что два или более потока найдут элементы, соответствующие критерию. Таким образом, вы должны выполнить сокращение мин на индекс:

int found = 0; 
int first_index = INVALID_VALUE; 
int iteration = 0; 

#pragma omp parallel 
{ 
    int my_index = INVALID_VALUE; 
    int i; 

    do 
    { 
     // Later versions of OpenMP allow for "atomic capture" 
     // but OpenMP 2.0 requires a critical directive instead 
     #pragma omp critical(iteration) 
     { 
     i = iteration++; 
     } 

     if (i < N && check(i)) 
     { 
     found = 1; 
     my_index = i; 
     } 
    } while (!found && i < N); 

    #pragma omp critical(reduction) 
    if (my_index != INVALID_VALUE) 
    { 
     if (first_index == INVALID_VALUE || my_index < first_index) 
     first_index = my_index; 
    } 

    // Only needed if more code follows before the end of the region 
    #pragma omp barrier 

    ... 
} 

Этот код предполагает, что проверка условия для г-го элемента (check(i)) не изменяет состояние элемента, и, следовательно, самое худшее, что может произойти, что поток, который нашел подходящий элемент, может ожидать, что все остальные потоки завершат проверку элемента, в котором они в настоящее время работают, и что время ожидания будет максимальным для всех периодов обработки.

Конструкция critical, используемая в do-loop, является дорогостоящей. Если check() не займет много времени, то вы могли бы рассмотреть возможность работы с кусками вместо итераций:

do 
{ 
    #pragma omp critical(chunk) 
    { 
     my_chunk = chunk++; 
    } 

    if (my_chunk >= N_chunks) 
     break; 

    for (i = my_chunk * chunk_size; !found && i < (my_chunk+1)*chunk_size; i++) 
    { 
     if (check(i)) 
     { 
     found = 1; 
     my_index = i; 
     break; 
     } 
    } 
} while (!found && my_chunk < N_chunks); 

Еще одно решение, которое работает достаточно хорошо, когда число элементов не так уж велик и проверка каждого из них дорого:

#pragma omp parallel 
{ 
    #pragma omp for schedule(dynamic,x) 
    for (i = 0; i < N; i++) 
    { 
     if (!found && check(i)) 
     { 
     my_index = i; 
     found = 1; 
     } 
    } 

    // Min reduction from the solution above 
    ... 
} 

После found становится истинным, остальные итерации цикла будет работать «пустые» тела, так как свойства shortcutting в &&.

+0

просто вопрос: для последнего решения вам не нужно нигде «найти»? – Gilles

+0

В дополнение к моему предыдущему комментарию, двум первым решениям не нужен «флеш», поскольку некоторые из них подразумеваются «критическими» разделами внутри циклов. Но для последнего решения нет неявного или явного флеша «найденного», который поэтому может оставаться нетронутым в кеше потоками, не найдя решения. По крайней мере, так я это вижу. Я ошибаюсь? – Gilles

+0

'found' был' volatile' изначально, затем я удалил его. Действительно, модель ослабленной памяти позволяет разным переменным иметь временно различающиеся значения в разных потоках, но правильное использование 'flush' является сложным, и я бы предпочел не вводить здесь конструкцию. Кроме того, большинство компиляторов OpenMP на x86 имеют тенденцию внедрять более строгую модель памяти (не сохраняя после назначения общих переменных только в регистрах), а 'flush' скомпилируется в забор памяти. Без флеша это может привести к дополнительной итерации, поэтому ничего особенного. Но ради правильности, да, должен быть один. –

0

С помощью OpenMP вы можете попытаться построить цикл с #pragma omp for schedule(dynamic). Каждый поток будет выполнять одну итерацию в том же порядке, что и ваш вектор. Если вы хотите проверить 4 элемента по теме, попробуйте #pragma omp for schedule(dynamic, 4)

+0

Спасибо, но он не уважает индексацию. Например, если номер потока 4 нашел элемент до потока номер 2. Я могу получить индекс отжима –

+0

Я думаю, что если условие в порядке, вы можете добавить '#pragma omp ordered' перед записью значения индекса и выхода из цикла , – Isukthar