Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sqlite/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 使用OpenMP原子操作获取和添加_C++_Atomic_Openmp_Compare And Swap - Fatal编程技术网

C++ 使用OpenMP原子操作获取和添加

C++ 使用OpenMP原子操作获取和添加,c++,atomic,openmp,compare-and-swap,C++,Atomic,Openmp,Compare And Swap,我正在使用OpenMP,需要使用fetch和add操作。但是,OpenMP没有提供适当的指令/调用。我希望保持最大的可移植性,因此我不想依赖于编译器内部函数 相反,我正在寻找一种方法来利用OpenMP的原子操作来实现这一点,但我遇到了死胡同。这能做到吗?注意,以下代码几乎满足了我的要求: #pragma omp atomic x += a 差不多–但不完全一样,因为我确实需要旧值x提取和添加以产生与以下相同的结果(仅非锁定): 模板 T取_和_加(易失性T&值,T增量){ T老; #pragm

我正在使用OpenMP,需要使用fetch和add操作。但是,OpenMP没有提供适当的指令/调用。我希望保持最大的可移植性,因此我不想依赖于编译器内部函数

相反,我正在寻找一种方法来利用OpenMP的原子操作来实现这一点,但我遇到了死胡同。这能做到吗?注意,以下代码几乎满足了我的要求:

#pragma omp atomic
x += a
差不多–但不完全一样,因为我确实需要旧值
x
<应定义代码>提取和添加以产生与以下相同的结果(仅非锁定):

模板
T取_和_加(易失性T&值,T增量){
T老;
#pragma-omp-critical
{
旧=价值;
值+=增量;
}
返老还童;
}

(如果我没有弄错的话,可以问一个等价的问题进行比较和交换,但一个可以用另一个来实现。)

如果您想得到x的旧值,并且a没有改变,请使用(x-a)作为旧值:

fetch_and_add(int *x, int a) {
 #pragma omp atomic
 *x += a;

 return (*x-a);
}
更新:这并不是一个真正的答案,因为x可以在原子后被另一个线程修改。 因此,似乎不可能使用OMP Pragmas实现通用的“获取和添加”。作为通用的,我指的是操作,它可以从OMP代码的任何位置轻松使用

您可以使用
omp\u*\ u lock
函数来模拟原子:

typedef结构{omp_lock_t lock;int value;}原子结构

fetch_and_add(atomic_simulated_t *x, int a)
{
  int ret;
  omp_set_lock(x->lock);
  x->value +=a;
  ret = x->value;
  omp_unset_lock(x->lock);
}
这是丑陋和缓慢的(做2个原子操作而不是1个)。但是,如果您希望您的代码非常可移植,那么它在所有情况下都不是最快的


您说“如下(仅非锁定)”。但是“非锁定”操作(使用CPU的“锁定”前缀,或LL/SC或等)和锁定操作(使用多个原子指令、短时间等待解锁的忙循环和长时间等待的操作系统睡眠)之间有什么区别呢?

从openmp 3.1开始,支持捕获原子更新,您可以捕获旧值或新值。因为我们必须从内存中引入值来增加它,所以我们应该能够从CPU寄存器访问它并将其放入线程私有变量中才有意义

如果您使用的是gcc(或g++),可以查找原子内置:

它认为英特尔的C/C++编译器也支持这一点,但我还没有尝试过

现在(直到OpenMP 3.1被实现),我在C++中使用了内联包装器函数,在这里你可以选择在编译时使用的版本:

template <class T>
inline T my_fetch_add(T *ptr, T val) {
  #ifdef GCC_EXTENSION
  return __sync_fetch_and_add(ptr, val);
  #endif
  #ifdef OPENMP_3_1
  T t;
  #pragma omp atomic capture
  { t = *ptr; *ptr += val; }
  return t;
  #endif
}
模板
内联T my_fetch_add(T*ptr,T val){
#ifdef GCC_扩展
返回、同步、获取和添加(ptr、val);
#恩迪夫
#ifdef OPENMP_3_1
T;
#pragma-omp原子捕获
{t=*ptr;*ptr+=val;}
返回t;
#恩迪夫
}

更新:我刚试用了英特尔的C++编译器,它目前支持OpenMP 3.1(实现原子捕获)。英特尔为非商业目的免费使用其linux编译器:


GCC 4.7最终发布时将支持openmp 3.1。。。希望很快:)

对于cas,openmp支持条件原子的一种变体,但只支持fortran语言。它是一个最小值和最大值;它们是有条件的。可以用来实现CAS操作的一个子集。@Konrad Rudolph,我也是,因为我需要1周的时间才能得到这个:)。另外,我所需要的步骤是学习不同平台上的LL/SC操作。@osgx:这实际上不是问题,因为您可以事先将类型强制为适当位大小的整数。当然,这取决于UB和平台,但有了适当的防护措施,这是一种非常可靠的技术。该死,这个答案也不正确:在原子更新和最后一行之间,另一个线程当然可能会修改
x
@康拉德·鲁道夫,是的,第一次尝试是错误的。更新了答案。我已经求助于使用GCC内置,但当然这对互操作性来说是可怕的。感谢OpenMP 3.1指针。不幸的是,由于VC++目前甚至不支持OpenMP 3,目前这是一个相当理论性的问题。仅适用于完整版本:它应该是
\ifdef\uu GNUC\uu
<代码>#elif定义(_OPENMP)和_OPENMP>=201107(适用于OPENMP 3.1)
#else#error“需要gcc或OpenMP>=3.1”#endif
。谢谢只是说,
atomic
并不是它的名字所承诺的那样,因为任何线程(在任何其他线程上)的
atomic
修改了内存后,都需要重新缓存。所以频繁和重复<代码>原子< /代码>可能会杀死你的性能(更好地使用锁和缓冲赛跑写)。@沃尔特,这也是我发现的经验:无锁算法执行等同于使用锁的等效算法。而无锁算法使用的同步要复杂得多——不是性能方面,而是逻辑方面(以及引入bug的机会)。
template <class T>
inline T my_fetch_add(T *ptr, T val) {
  #ifdef GCC_EXTENSION
  return __sync_fetch_and_add(ptr, val);
  #endif
  #ifdef OPENMP_3_1
  T t;
  #pragma omp atomic capture
  { t = *ptr; *ptr += val; }
  return t;
  #endif
}