#c #openmp
Вопрос:
Рассмотрим следующий фрагмент:
#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
она не является неполной. Я также могу скомпилировать, если карта является примитивным типом (double, int…).
Как мне сделать глобальную статику std::map
threadprivate
?
Ответ №1:
Это ограничение компилятора. Компилятор 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
будут расположены в другой строке кэша, что не приведет к ложному обмену данными. ССАГПЗ имеет __attribute__ ((aligned(64)))
.
Чтобы получить доступ к этому простому протоколу TLS, вы можете сделать это:
tls[omp_get_thread_num()].theMap;
Конечно, вы должны вызвать это внутри одной из параллельных конструкций OpenMP. Приятно то, что OpenMP предоставляет абстрактный идентификатор потока в [0, N), где N — максимальное число потоков. Это обеспечивает быструю и простую реализацию TLS. В общем случае собственный идентификатор TID операционной системы представляет собой произвольное целое число. Таким образом, вам в основном нужна хэш-таблица, время доступа к которой больше, чем у простого массива.
Ответ №2:
Ошибка неполного типа-это ошибка в компиляторе, которую можно обойти, создав экземпляр std::map<int,int>
перед директивой threadprivate. Но как только вы преодолеете эту проблему, GCC 4.7 по-прежнему не поддерживает динамическую инициализацию переменных threadprivate. Это будет поддерживаться в GCC 4.8.
Ответ №3:
Все, что связано с потоком, будет реплицировано для каждого потока. Я сделал это, создав статический объект (класс не обязательно должен быть статичным, просто созданный объект должен быть статичным). Может быть, это то, чего ты хочешь?
Теперь подумайте, хотите ли вы, чтобы некоторые члены класса были разделены между потоками. Создание статических только некоторых членов класса подразумевает, что если каждый поток создал экземпляр этого объекта, то мы должны реплицировать только статическую часть (поскольку она является частной), но не весь объект (общая память не реплицируется). Это потребовало бы, чтобы у одного объекта было все, а все остальные объекты были меньшего размера (без повторного хранения общей памяти), но все равно имели ссылку на общую память, что, откровенно говоря, не имеет смысла.
В качестве предложения создайте себе два класса: один с строго (потоковыми)личными данными и один для общих данных.
Комментарии:
1. В реальном коде, который я пишу,
class A
на самом деле является абстрактной фабрикой и имеет только статические члены и методы. Я мог бы превратить его в синглтон, но я хотел бы избежать переписывания кода, если есть очевидный обходной путь OpenMP, который дает вамthreadprivate
контейнеры STL.