Распараллеливание OpenMP на рекурсивной функции

Я пытаюсь использовать распараллеливание, чтобы улучшить частоту обновления для рисования 3D-сцены с heirarchically упорядоченными объектами. Алгоритм рисования сцены сначала рекурсивно пересекает дерево объектов и из него строит упорядоченный массив необходимых данных, необходимых для рисования сцены. Затем он пересекает этот массив несколько раз, чтобы нарисовать объекты / наложения и т. д. Поскольку из того, что я прочитал OpenGL, не является потокобезопасным API, я предполагаю, что код обхода/рисования массива должен быть выполнен на основной поток, но я думаю, что я мог бы выполнять рекурсивную функцию, которая заполняет массив. Ключевой момент заключается в том, что массив должен быть заполнен в том порядке, в котором объекты встречаются в сцене, поэтому все функции, которые связывают данный объект с индексом массива, должны выполняться в правильном порядке, но как только индекс массива назначен, я могу заполнить данные этого элемента массива (что не обязательно является тривиальной операцией) с помощью рабочих потоков. Вот псевдо код это я и пытаюсь понять. Надеюсь, вы получите представление о синтаксисе потока xml-ish.

recursivepopulatearray(theobject)
{
  <main thread>
  for each child of theobject
  {
     assign array index
     <child thread(s)>
       populate array element for child object
     </child thread(s)>
     recursivepopulatearray(childobject)
  }
  </main thread>
}

Итак, можно ли это сделать с помощью OpenMP, и если да, то как? Существуют ли другие библиотеки распараллеливания, которые справились бы с этим лучше?

дополнение: в ответ просьба Давиде о дополнительных разъяснениях, позвольте мне объяснить немного подробнее. Предположим, что сцена упорядочена следующим образом:

-Bicycle Frame
  - Handle Bars 
  - Front Wheel
  - Back Wheel
-Car Frame
  - Front Left Wheel
  - Front Right Wheel
  - Back Left Wheel
  - Back Right Wheel

Теперь, каждый из этих объектов имеет много данных, связанных с ним, т. е. местоположение, вращение, размер, различные параметры чертежа и т. д. Кроме того, мне нужно сделать несколько проходов над этой сценой, чтобы нарисовать его правильно. Один проход рисует формы объектов, другой проход рисует текст, описывающий объекты, другой проход рисует связи/ассоциации между объектами, если они есть. В любом случае, получение всех данных рисования из этих разных объектов довольно медленно, если мне нужно получить доступ к нему несколько раз, поэтому я решил используйте один проход, чтобы кэшировать все эти данные в одномерный массив, а затем все фактические проходы рисования просто смотрят на массив. Уловка в том, что, поскольку мне нужно сделать OpenGL pushes/pops в правильном порядке, массив должен быть в правильном порядке поиска глубины, который является представителем древовидной heirarchy. В приведенном выше примере, массив должен быть упорядочен следующим образом:

index 0: Bicycle Frame
index 1: Handle Bars 
index 2: Front Wheel
index 3: Back Wheel
index 4: Car Frame
index 5: Front Left Wheel
index 6: Front Right Wheel
index 7: Back Left Wheel
index 8: Back Right Wheel

Итак, порядок массива должен быть сериализован должным образом, но как только я назначил этот порядок правильно, я могу распараллелить заполнение массива. Например, как только я назначил велосипедную рамку индексу 0 и ручные бары индексу 1, один поток может взять заполнение элемента массива для велосипедной рамки, а другой-заполнение элемента массива для ручек.

хорошо, я думаю, проясняя это, я ответил на свой вопрос, так что спасибо Давиде. Поэтому я разместил свой собственный ответ.

4 ответов


Я думаю, вы должны лучше прояснить свой вопрос (например, что именно нужно делать последовательно и почему)

OpenMP (как и многие другие библиотеки распараллеливания) делает не гарантируйте порядок, в котором будут выполняться различные параллельные секции, и поскольку они действительно параллельны (на многоядерной машине), могут быть условия гонки, если разные секции пишут одни и те же данные. Если это нормально для вашей проблемы, конечно, вы можете использовать его.


As gbjbaanb упоминается, вы можете сделать это легко - для этого просто требуется оператор pragma для распараллеливания этого.

тем не менее, есть несколько вещей, чтобы следить за:

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

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

Если вы собираетесь это сделать, вы, вероятно, должны поставить параметр level и только распараллелить первые пару уровней. Возьмите мои 4 ребенка-в неполных примеру, если распараллелить первые 2 уровня, вы уже разбиваете это на 16 параллельных кусков (называемых из 4 параллельных кусков).

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

" затем он пересекает этот массив несколько раз для рисования объектов / наложений и т. д."

Это звучит как идеальное место для распараллеливания.


чтобы распараллелить дочерний поток, просто поместите прагму перед циклом:

#pragma omp parallel for
for (i=0; i < elements; i++) 
{
}

работу.

теперь вы совершенно правы, вы не можете заставить библиотеку потоков делать один бит перед другим полностью параллельным способом (очевидно!), и openMP не имеет функции "lock" или "wait" (у него есть ключевое слово - барьер "wait for all to finish"), он не предназначен для эмуляции библиотеки потоков, но позволяет хранить значения " вне " параллельного раздела и отметьте некоторые разделы как "только однопоточные" (упорядоченное ключевое слово), чтобы это могло помочь вам назначить индексы в параллельном цикле, в то время как другие потоки назначают элементы.

посмотреть руководство по началу работы.

Если вы используете Visual C++, вам также необходимо установить флаг /omp в настройках сборки компилятора.


вот модифицированный фрагмент псевдо-кода, который должен работать.

populatearray(thescene)
{
  recursivepopulatearray(thescene)

  #pragma omp parallel for
  for each element in array
    populate array element based on associated object
}

recursivepopulatearray(theobject)
{
  for each childobject in theobject
  {
     assign array index and associate element with childobject
     recursivepopulatearray(childobject)
  }
}