在C ++ STL类型的静态实例上使用OpenMP threadprivate指令

时间:2011-11-08 13:26:50

标签: c++ stl 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 不完整。如果Map是基本类型(double,int ...),我也可以编译。

如何制作全局静态std::map threadprivate

3 个答案:

答案 0 :(得分:2)

这是编译器限制。英特尔C / C ++编译器支持threadprivate上的C ++类,而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];

请注意,我填充的原因是为了避免false sharing。我假设现代Intel x86处理器的64字节高速缓存行。 __declspec(align(64))是MSVC扩展,结构位于64的边界上。因此,tls中的任何元素都将位于不同的缓存行上,从而不会产生错误共享。海湾合作委员会有__attribute__ ((aligned(64)))

要访问这个简单的TLS,您可以这样做:

tls[omp_get_thread_num()].theMap;

当然,你应该在一个OpenMP并行结构中调用它。好处是OpenMP在[0,N)中提供了抽象的线程ID,其中N是最大线程数。这样可以实现快速简单的TLS实现。通常,来自操作系统的本机TID是任意整数。因此,您通常需要一个哈希表,其访问时间比简单数组长。

答案 1 :(得分:1)

不完整类型错误是编译器中的错误,可以通过在threadprivate指令之前实例化std::map<int,int>来解决。但是一旦你解决了这个问题,GCC 4.7仍然不支持threadprivate变量的动态初始化。这将在GCC 4.8中得到支持。

答案 2 :(得分:0)

将为每个线程复制任何threadprivate。我通过创建一个静态对象(类不需要是静态的,只是实例化的对象必须是静态的)来完成这个。也许这就是你想要的?

现在考虑是否希望在线程之间共享类的某些成员。只使类的某些成员静态意味着如果每个线程实例化该对象,那么我们应该仅复制静态部分(因为它的threadprivate)而不是整个对象(不复制共享内存)。这将需要一个对象使所有内容和所有其他对象具有更小的尺寸(不重新存储共享内存),但仍然具有对共享内存的引用,这很坦率地说没有意义。

作为建议,请自己创建两个类,一个是严格(线程)私有数据,另一个是共享数据。