我正在使用 OpenMP 来并行化我们的 C++ 库。在那里,我们有很多地方可以通过将结果存储在变量中来避免重新计算某些东西(即缓存结果以供重复使用)。但是,这种行为在类的方法中对用户是隐藏的。例如,在第一次使用方法时,缓存将被填充。所有后续使用都将从缓存中读取。
我现在的问题是,在多线程程序中,多个线程可以同时调用这样的方法,导致创建/访问缓存时出现竞争条件。我目前正在通过将缓存内容放在关键部分来解决这个问题,但这当然会减慢一切。
一个示例类可能如下所示
class A {
public:
A() : initialized(false)
{}
int get(int a)
{
#pragma omp critical(CACHING)
if (!initialized)
initialize_cache();
return cache[a];
}
private:
bool initialized;
void initialize_cache()
{
// do some heavy stuff
initialized=true;
}
int *cache;
};
如果关键部分在 initialize_cache() 函数中会更好,因为那样它只会在缓存尚未初始化时锁定所有线程(即仅一次),但这看起来很危险,因为多个线程可能正在尝试同时初始化缓存。
有什么改进的建议吗?理想情况下,该解决方案将与旧的 OpenMP 版本兼容(甚至是 Visual Studio 的 v2...)
PS:以前可能有人问过这个问题,但是搜索 openmp 和缓存会在处理器缓存上抛出很多东西,这不是我想知道的...
最佳答案
您可以使用 "Double-Checked-Locking(DCL) pattern"使用 OpenMP 原子操作,需要 OpenMP v3.1 或更高版本(omp atomic
pragma 的read
/write
选项)。
class A {
public:
A() : initialized(false)
{}
int get(int a)
{
bool b;
#pragma omp atomic read
b = initialized;
if (!b) {
#pragma omp critical(CACHING)
// you must recheck in critical section
if (!initialized)
initialize_cache();
}
return cache[a];
}
private:
bool initialized;
void initialize_cache()
{
// do some heavy stuff
#pragma omp atomic write
initialized = true;
}
int *cache;
};
...但我推荐以下选项之一而不是 DCL 模式:
-
pthread_once()
(POSIX 线程库) -
std::call_once()
(C++11 标准库) - 线程安全的
static
变量(C++11核心语言特性)
关于c++ - 如何使用多线程处理缓存数据结构(例如 openmp),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27975737/