理解浮法还原OpenCL的方法

理解浮法还原OpenCL的方法,opencl,atomic,Opencl,Atomic,接下来,我试图了解内核代码的操作(该内核代码有两个版本,一个版本具有易失性局部浮点*源代码,另一个版本具有易失性全局浮点*源代码,即局部和全局版本)。下面我采用本地版版本: float sum=0; void atomic_add_local(volatile local float *source, const float operand) { union { unsigned int intVal; float floatVal; } newV

接下来,我试图了解内核代码的操作(该内核代码有两个版本,一个版本具有
易失性局部浮点*源代码
,另一个版本具有
易失性全局浮点*源代码
,即
局部
全局
版本)。下面我采用
本地版
版本:

float sum=0;
void atomic_add_local(volatile local float *source, const float operand) {
    union {
        unsigned int intVal;
        float floatVal;
    } newVal;

    union {
        unsigned int intVal;
        float floatVal;
    } prevVal;

    do {
        prevVal.floatVal = *source;
        newVal.floatVal = prevVal.floatVal + operand;
    } while (atomic_cmpxchg((volatile local unsigned int *)source, prevVal.intVal, newVal.intVal) != prevVal.intVal);
}
如果我理解得很好,由于限定符“
volatile
”,每个工作项共享对
变量的访问,不是吗

之后,如果我获取一个工作项,代码将向
newVal.floatVal
变量添加
operand
值。然后,在这个操作之后,我调用
atomic_cmpxchg
函数,检查之前的赋值(
preVal.floatVal=*source;
newVal.floatVal=preVal.floatVal+operand;
)是否已经完成,即通过比较存储在地址
source
处的值和
preVal.intVal

在这个原子操作过程中(根据定义,它不是不可中断的),由于存储在
的值不同于
prevVal.intVal
,存储在
的新值是
newVal.intVal
,它实际上是一个浮点值(因为它是以类似整数的4个字节编码的)

我们可以说每个工作项对位于
源地址的值有一个互斥访问(我指的是锁定访问)吗

但是对于
每个工作项
线程,是否只有一次迭代进入
while循环

我认为会有一次迭代,因为比较“
*source==prevVal.int?newVal.intVal:newVal.intVal
”总是将
newVal.intVal
值分配给存储在
源地址的值,不是吗

欢迎提供任何帮助,因为我还没有理解这个内核代码的所有微妙之处

更新1:

抱歉,我几乎理解了所有的细微之处,尤其是在
while循环中:

第一种情况:对于给定的单个线程,在调用atomic_cmpxchg之前,如果
prevVal.floatVal
仍然等于
*source
,则
atomic_cmpxchg
将更改
source
指针中包含的值,并返回
旧指针中包含的值,这等于
prevVal.intVal
,因此我们中断了
while循环

第二种情况:如果在
prevVal.floatVal=*源之间指令和调用
atomic_cmpxchg
,值
*source
已更改(由另一个线程更改),然后atomic_cmpxchg返回
旧的
值,该值不再等于
prevVal.floatVal
,因此,进入
while循环的条件是真的,我们将保持在这个循环中,直到前面的条件不再被检查为止

我的解释正确吗

谢谢

如果我能很好地理解的话,由于限定符“
volatile
”,每个工作项共享对源变量的访问,不是吗

volatile
是C语言的一个关键字,它阻止编译器优化对内存中特定位置的访问(换句话说,在所述内存位置的每次读/写时强制加载/存储)。它对基础存储的所有权没有影响。在这里,它用于强制编译器在每次循环迭代时从内存中重新读取
源代码
(否则编译器将被允许将该负载移到循环之外,从而破坏算法)

在删除限定符(为简单起见)并重命名参数后,
atomic_cmpxchg
的签名如下:

int atomic_cmpxchg(int *ptr, int expected, int new)
它所做的是:

atomically {
    int old = *ptr;

    if (old == expected) {
        *ptr = new;
    }

    return old;
}
总而言之,每个线程分别执行以下操作:

  • *source
    的当前值从内存加载到
    preVal.floatVal
  • newVal.floatVal
  • 执行上述原子比较交换(使用类型punned值)
  • 如果原子的结果=newVal.intVal
  • ,则表示比较交换成功,请中断。否则,交换未发生,请转到1并重试 上面的循环最终终止,因为最终每个线程都成功地执行了它们的
    原子\u cmpxchg

    我们可以说每个工作项对位于源地址的值都有一个互斥访问(我指的是锁定访问)


    互斥锁是锁,而这是一种无锁算法。OpenCL可以用自旋锁(也用原子实现)模拟互斥锁,但这不是一个。

    如果这对您来说是显而易见的(我想我还没有完全理解这个问题),那么很抱歉,但是。。。
    while
    循环是实现原子性的标准方法,从:Marco13、:Aldwin ok开始,谢谢。让我们简单地使用2个线程。如果第一个线程在while循环中,那么,直到第二个线程修改了“prevVal.floatVal”的值,while循环才持续到第一个线程,不是吗?但在本例中,递增“prevVal.floatVal+operand;”的运算是无限的(直到第二个线程停止它),因此存储在地址“source”的值非常高,因为我对大量“operand”值进行求和。关于“如果原子的结果=newVal.intVal,这意味着比较交换成功,用”do{}while(原子的)(易失性局部无符号int*)源,prevVal.intVal,newVal.intVal)!=prevVal.intVal”来表示,但我认为我们应该改为“do{}while(原子的){cmpxchg((易失性局部无符号int*)源,prevVal.intVal)!=prevVal.intVal)”((volatile local unsigned int*)source,prevVal.intVal,newVal.intVal)==prevVal.intVal)“因为当atomic_cmpxchg的结果为newVal.intVal时,我们中断了while循环,如果“*source==preVal.floatVal”,不是吗?我想我已经理解了,你能在上面的更新1中看到我的解释吗?谢谢
    atomically {
        int old = *ptr;
    
        if (old == expected) {
            *ptr = new;
        }
    
        return old;
    }