Как реализовать «мягкий барьер» в многопоточном C ++ - PullRequest
4 голосов
/ 14 ноября 2011

У меня есть несколько многопоточных кода C ++ со следующей структурой:

do_thread_specific_work();
update_shared_variables();
//checkpoint A
do_thread_specific_work_not_modifying_shared_variables();
//checkpoint B
do_thread_specific_work_requiring_all_threads_have_updated_shared_variables();

То, что следует за контрольной точкой B, - это работа, которая могла бы начаться, если бы все потоки достигли только контрольной точки A, отсюда мое понятие «мягкого барьера».

Как правило, многопоточные библиотеки предоставляют только «жесткие барьеры», в которых все потоки должны достигнуть некоторой точки, прежде чем любая из них сможет продолжить работу. Очевидно, что на контрольно-пропускном пункте В может быть использован жесткий барьер.

Использование мягкого барьера может привести к сокращению времени выполнения, тем более что работа между контрольными точками A и B может быть не сбалансирована нагрузкой между потоками (т. Е. 1 медленный поток, достигший контрольной точки A, но не B, может вызывать все другие подождать у барьера непосредственно перед контрольно-пропускным пунктом B).

Я пытался использовать атомику для синхронизации вещей, и я знаю со 100% уверенностью, что это НЕ гарантированно работает. Например, используя синтаксис openmp, перед началом параллельного раздела:

shared_thread_counter = num_threads;  //known at compile time
#pragma omp flush

Затем на контрольной точке A:

#pragma omp atomic
shared_thread_counter--;

Затем в контрольной точке B (с использованием опроса):

#pragma omp flush
while (shared_thread_counter > 0) {
  usleep(1);  //can be removed, but better to limit memory bandwidth
  #pragma omp flush
}

Я разработал несколько экспериментов, в которых я использую атом, чтобы указать, что какая-то операция до ее завершения. Эксперимент будет работать с двумя потоками в большинстве случаев, но постоянно терпит неудачу, когда у меня много потоков (например, 20 или 30). Я подозреваю, что это из-за структуры кэширования современных процессоров. Даже если один поток обновляет какое-то другое значение перед выполнением атомарного декремента, другой поток в этом порядке не гарантируется. Рассмотрим случай, когда другое значение является пропуском кеша, а атомарный декремент - попаданием в кеш.

Итак, вернемся к моему вопросу, как ПРАВИЛЬНО реализовать этот «мягкий барьер»? Есть ли встроенная функция, которая гарантирует такую ​​функциональность? Я бы предпочел openmp, но я знаком с большинством других распространенных многопоточных библиотек.

В качестве обходного пути сейчас я использую жесткий барьер на контрольной точке B и реструктурировал свой код, чтобы заставить работу между контрольной точкой A и B автоматически распределять нагрузку между потоками (что иногда было довольно сложно ).

Спасибо за любой совет / понимание:)

1 Ответ

2 голосов
/ 14 ноября 2011

Как насчет использования условной переменной? Я не уверен, предоставляется ли условная переменная, так как я не знаком с OpenMP.

int counter = 0;
condition_variable cond;

// checkpoint A
++counter;
cond.notify_all();

// checkpoint B
cond.wait_until( counter >= NUM_THREADS );

Прежде чем каждый поток достигнет контрольной точки A, ни один поток не может пройти через контрольную точку B.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...