2013-03-04 3 views
10

У меня есть программа на C++, которая может быть распараллелена. Я использую компиляцию Visual Studio 2010, 32bit.Параллельные задачи улучшают производительность с boost :: thread, чем с ppl или OpenMP

В короткой структуре программы является следующая

#define num_iterations 64 //some number 

struct result 
{ 
    //some stuff 
} 

result best_result=initial_bad_result; 

for(i=0; i<many_times; i++) 
{ 
    result *results[num_iterations]; 


    for(j=0; j<num_iterations; j++) 
    { 
     some_computations(results+j); 
    } 

    // update best_result; 
} 

Поскольку каждый some_computations() является независимым (некоторые глобальные переменные чтения, но никакие глобальные переменные не будут изменены) Я распараллелен внутреннюю for -loop.

Моя первая попытка была с импульс :: нить,

thread_group group; 
for(j=0; j<num_iterations; j++) 
{ 
    group.create_thread(boost::bind(&some_computation, this, result+j)); 
} 
group.join_all(); 

Результаты были хорошими, но я решил попробовать больше.

Я попробовал библиотеку OpenMP

#pragma omp parallel for 
for(j=0; j<num_iterations; j++) 
{ 
    some_computations(results+j); 
} 

результаты были хуже, чем те, boost::thread «s.

Тогда я попробовал Закон о госзакупках библиотеку и использовали parallel_for():

Concurrency::parallel_for(0,num_iterations, [=](int j) { 
    some_computations(results+j); 
}) 

Результаты были хуже.

Я нашел это поведение совершенно неожиданным. Поскольку OpenMP и ppl предназначены для распараллеливания, я ожидал бы лучших результатов, чем boost::thread. Я ошибаюсь?

Почему boost::thread дает мне лучшие результаты?

+2

Не могли бы вы подсчитать «лучше», например? обеспечить время выполнения по сравнению с количеством потоков? С 'boost :: thread' вы создаете 64 потока. OpenPM использует команду рабочих потоков, число которых по умолчанию соответствует количеству виртуальных процессоров. PPL также использует пул потоков и имеет еще более высокие накладные расходы, чем OpenMP, поскольку он также реализует балансировку работы. –

+0

Я использовал один и тот же номер (32 или 64) для каждой попытки, возможно, как вы указали, с помощью OpenMP и ppl я мог бы получить лучшие результаты, устанавливая количество потоков, равное количеству ядер. Я попробую. – 888

+1

Почти невозможно ответить на вопрос, поскольку он стоит. Что такое 'some_computations'? Я где-то встречаю возможное соперничество (которое может поразить разные библиотеки по-разному, например, если у openmp на самом деле более низкие накладные расходы, но у вас много записей в общих кешлях, что может привести к замедлению в результате недействительности кэширования)? Сколько времени требуется для выполнения параллельного блока для каждого варианта – Grizzly

ответ

9

OpenMP или PPL не имеют такой вещи, как пессимизм. Они просто делают, как им говорят, однако есть некоторые вещи, которые вы должны учитывать, когда пытаетесь парализовать циклы.

Не видя, как вы реализовали эти вещи, трудно сказать, какова может быть настоящая причина.

Также, если операции на каждой итерации имеют некоторую зависимость от любых других итераций в одном цикле, тогда это создаст конкуренцию, что замедлит работу. Вы не показали, что делает ваша функция some_operation, поэтому трудно определить, существуют ли зависимости данных.

Цикл, который может быть по-настоящему распараллелен, должен иметь возможность выполнять каждый итерационный запуск полностью независимо от всех других итераций, без доступа к общей памяти в любой из итераций. Поэтому желательно, чтобы вы записывали материал в локальные переменные, а затем копировали в конце.

Не все петли могут быть распараллелены, они очень зависят от типа выполняемой работы.

Например, что-то, что хорошо для распараллеливания, это работа, выполняемая на каждом пикселе экранного буфера. Каждый пиксель полностью независим от всех других пикселей, и поэтому поток может принимать одну итерацию цикла и выполнять работу без необходимости удерживаться в ожидании разделяемой памяти или зависимостей данных в цикле между итерациями.

Кроме того, если у вас есть непрерывный массив, этот массив может быть частично в строке кэша, и если вы редактируете элемент 5 в потоке A, а затем изменяете элемент 6 в потоке B, вы можете получить утверждение о кеше, которое будет также замедляют работу, поскольку они будут находиться в одной и той же строке кэша. Явление, известное как ложное разделение.

Существует много аспектов, которые следует учитывать при выполнении параллелизации цикла.

+0

, который вы используете 'some_operation', принимает смещение в массив, и массив распределяется между несколькими потоками. Я не знаю, что либо PPL, либо OpenMP могут создавать любые garantuees, которые вы не пишете в этот массив, или что что-либо еще записывает в этот массив. Поэтому мой ответ не меняется. –

+1

Ваш первый абзац неверен. Ни OpenMP, ни PPL не заботятся о том, что вы делаете с общими переменными, и нет ничего пессимистичного или оптимистичного в том, как они работают. Оба являются императивными концепциями программирования, а это значит, что компилятор делает код параллельным, если говорить так, а не рассматривать выражения так же, как подсказки. Надлежащее отношение к общим переменным предоставляется исключительно программисту. –

2

Вкратце, openMP в основном основан на общей памяти, с дополнительными затратами на управление задачами и управление памятью. ppl предназначен для обработки общих шаблонов общих структур данных и алгоритмов, что приносит дополнительную сложность. У обоих из них есть дополнительная стоимость ЦП, но ваши простые падения вниз boost нитей нет (boost потоки - это просто простая упаковка API). Вот почему они оба медленнее, чем ваша версия boost. И поскольку расчетные вычисления независимы друг от друга, без синхронизации, openMP должен быть близок к версии boost.

Это происходит в простых сценариях, но для сложных сценариев со сложной компоновкой данных и алгоритмами она должна быть зависимой от контекста.

+2

OpenMP не предназначен для передачи сообщений, MPI - это тот, который передает файлы. – Moss

+1

@Moss, спасибо, я перепутал OpenMP и MPI. OpenMP основан на основе общей памяти. –