Использование директивы OpenMP threadprivate для статических экземпляров типов C ++ STL - PullRequest
6 голосов
/ 08 ноября 2011

Рассмотрим следующий фрагмент:

#include <map>

class A {
    static std::map<int,int> theMap;
#pragma omp threadprivate(theMap)
};

std::map<int,int> A::theMap;

Компиляция с OpenMP завершается неудачно со следующим сообщением об ошибке:

$ g++ -fopenmp -c main.cpp 
main.cpp:5:34: error: ‘threadprivate’ ‘A::theMap’ has incomplete type

Я не понимаю этого. Я могу скомпилировать без директивы #pragma, что должно означать, что std::map является не неполным. Я также могу скомпилировать, если theMap является примитивным типом (double, int ...).

Как сделать глобальную статическую std::map threadprivate?

Ответы [ 3 ]

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

Это ограничение компилятора.Компилятор Intel C / C ++ поддерживает классы C ++ на threadprivate, в то время как gcc и MSVC в настоящее время не могут.

Например, в MSVC (VS 2010) вы получите эту ошибку (я удалил класс):

static std::map<int,int> theMap;
#pragma omp threadprivate(theMap)

error C3057: 'theMap' : dynamic initialization of 'threadprivate' symbols is not currently supported

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

const static int MAX_THREAD = 64;

struct MY_TLS_ITEM
{
  std::map<int,int> theMap;
  char padding[64 - sizeof(theMap)];
};

__declspec(align(64)) MY_TLS_ITEM tls[MAX_THREAD];

Обратите внимание, что причина, по которой у меня есть заполнение, заключается в том, чтобы избежать ложного совместного использования .Я предполагаю, что 64-байтовая строка кэша для современных процессоров Intel x86.__declspec(align(64)) является расширением MSVC, структура которого находится на границе 64. Таким образом, любые элементы в tls будут расположены на другой строке кэша, что не приведет к ложному совместному использованию.GCC имеет __attribute__ ((aligned(64))).

Чтобы получить доступ к этому простому TLS, вы можете сделать это:

tls[omp_get_thread_num()].theMap;

Конечно, вы должны вызвать это внутри одного изOpenMP параллельные конструкции.Приятно то, что OpenMP предоставляет абстрагированный идентификатор потока в [0, N), где N - максимальный номер потока.Это обеспечивает быструю и простую реализацию TLS.Как правило, собственный TID операционной системы - это произвольное целое число.Таким образом, вам в основном нужна хеш-таблица, время доступа которой больше, чем у простого массива.

1 голос
/ 15 октября 2012

Ошибка неполного типа - это ошибка в компиляторе, которую можно обойти, создав экземпляр std::map<int,int> перед директивой threadprivate.Но как только вы обойдете эту проблему, GCC 4.7 по-прежнему не поддерживает динамическую инициализацию переменных Threadprivate.Это будет поддерживаться в GCC 4.8.

0 голосов
/ 08 ноября 2011

Все, что принадлежит потоку, будет реплицировано для каждого потока.Я сделал это, создав статический объект (класс не обязательно должен быть статическим, просто объект должен быть статическим).Может быть, это то, что вы хотите?

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

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

...