理解浮法还原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;
}