Общие данные OpenMP

#c #openmp #shared

#c #openmp #общие

Вопрос:

Я несколько новичок в OpenMP, но имею опыт работы с параллельной обработкой в целом. Я работал с boost::threads раньше, и теперь я тестирую с openmp.

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

Что я делаю прямо сейчас (пока это работает): я считываю файлы с диска в память с помощью mmap. Я получаю указатель на символ после части карты памяти.

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

Я сделал этот список (вектор, содержащий регулярное выражение) закрытым внутри цикла openmp, поэтому у каждого потока есть своя копия этого списка.

Здесь возникает проблема:

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

Теперь всем другим активным потокам также необходимо удалить этот элемент из своего списка как можно скорее.

Итак, я сделал этот список общим объектом данных внутри цикла openmp, но теперь я получаю ошибки сегментации во время выполнения, когда я пытаюсь записать (vector.erase(item #)) в список.

С boost:: threads я бы просто использовал мьютекс для блокировки этого объекта во время его записи / чтения.

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

Комментарии:

1. У вас есть минимальный пример, демонстрирующий проблему?

Ответ №1:

Для синхронизации вы можете использовать #pragma omp critical или вы можете использовать процедуры блокировки OpenMP ( omp_{init,set,unset,destroy}_lock ).

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

Процедуры блокировки OpenMP аналогичны большинству других доступных блокировок, например, в pthreads или Boost (исключая RAII). Вы инициализируете объект блокировки, затем используете его для защиты определенных критических разделов и уничтожаете, когда в этом нет необходимости. Эти блокировки могут использоваться для защиты доступа к данным из разных параллельных областей, для построения распределенной схемы блокировки и т.д.; Но всегда возникают определенные накладные расходы, и, безусловно, использование является более «волосатым» по сравнению с #pragma omp critical .

Однако я бы поставил под сомнение дизайн параллельного решения. Удаление элемента из середины вектора делает все итераторы недействительными и перемещает элементы. Предположительно, удаление является редкой операцией (в противном случае, выбор вектора был бы сомнительным даже в последовательном коде, я думаю), но из-за вышеуказанных эффектов вы также должны защищать все чтения вектора, и это, вероятно, будет дорогостоящим. Блокировки чтения / записи могли бы принести некоторое облегчение, но они недоступны в OpenMP, поэтому вам нужно будет использовать либо интерфейсы, зависящие от платформы, либо стороннюю библиотеку.

Я думаю, что следующее потенциально будет работать лучше:

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

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

Ответ №2:

Вы можете достичь этого, создав критический раздел.

 #pragma omp critical
{
   ...some synchronized code...
}
  

Редактировать:
Удалена часть о ‘#pragma omp atomic’, поскольку она не может атомарно выполнять необходимые операции.

Комментарии:

1. #pragma omp atomic очень ограничен в том, какие выражения там разрешены. Приведенный вами пример не соответствует спецификации OpenMP. Я предлагаю вам удалить соответствующую часть из ответа.