2017-01-30 78 views
0

this link,我试着去理解内核代码的工作(有2个版本,这个内核代码,一个与volatile local float *source和其他与volatile global float *source,即localglobal版本)。下面我把local版本:了解针对OpenCL减少方法上浮动

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); 
} 

如果我没理解好,每个工作项目股份获得source变量感谢限定词“volatile”,不是吗?

之后,如果我带一个工作项目,代码会将operand值添加到newVal.floatVal变量。然后,在此操作之后,我呼叫atomic_cmpxchg函数,该函数检查以前的分配(preVal.floatVal = *source;newVal.floatVal = prevVal.floatVal + operand;)是否已完成,即通过将存储在地址source的值与preVal.intVal进行比较。

在该原子操作(这是不被定义不间断),为存储在source值是从prevVal.intVal不同,储存在source新值newVal.intVal,这实际上是一个浮子(因为它是在4个字节等编码整数)。

我们可以说,每个工作项目都有一个互斥体访问(我的意思是一个锁定访问)的价值位于source address

但是对于each work-item线程,是否只有一个迭代进入while loop

我想会有一个迭代,因为比较“*source== prevVal.int ? newVal.intVal : newVal.intVal”将始终将newVal.intVal值分配给存储在source address的值,不是吗?

任何帮助都是值得欢迎的,因为我没有理解这个内核代码的所有细节。

更新1:

对不起,我几乎了解所有的subtilities,尤其是在while loop

第一种情况:对于给定的单线程,atomic_cmpxchg的调用之前,如果prevVal.floatVal仍然等于*source,则atomic_cmpxchg将更改指针中source中包含的值并返回包含在old pointer中的值,这等于prevVal.intVal,所以我们从while loop

第二种情况:如果prevVal.floatVal = *source;指令和atomic_cmpxchg呼叫之间,该值已*source(由另一个线程??)改变,则返回atomic_cmpxchg值old它是没有到prevVal.floatVal更多相等,所以该条件成while loop是真的,我们留在这个循环中,直到之前的条件不再被检查。

我的解释是否正确?

感谢

+1

对不起,如果这对你来说很明显(我想我还没有完全理解这个问题),但是...... while循环是实现原子性的一种标准方式,例如https://en.wikipedia。 org/wiki /比较和交换 – Marco13

+0

这是Marco提到的经典比较交换循环。为了清楚起见,忽略联合技巧,他们只是在这里进行类型双关。另外,如果你有OpenCL 2+,那么浮点数就有内建原子。 –

+0

:Marco13,:Aldwin好的,谢谢。让我们用一个简单的例子来说明两个线程。如果第一个在while循环中,那么,直到第二个修改“prevVal.floatVal”的值,while循环持续第一个线程,不是吗?但是在这种情况下,增量操作“prevVal.floatVal + operand;”是无限的(直到第二个线程停止它),因此存储在地址“source”的值非常高,因为我用大量的“操作数”值进行求和。问候 – youpilat13

回答

1

如果我没理解好,每个工作项目股份访问变量由于源到限定词“volatile”,不是吗?

volatile是防止从在存储器存取最佳化到特定的位置,编译器C语言的关键字(换言之,力在每个加载/存储读取所述存储器位置的/写)。它不会影响底层存储的所有权。在这里,它用于强制编译器在每次循环迭代时从内存中重新读取source(否则编译器将被允许将该负载移到循环外部,这打破了该算法)。

do { 
    prevVal.floatVal = *source; // Force read, prevent hoisting outside loop. 
    newVal.floatVal = prevVal.floatVal + operand; 
} while(atomic_cmpxchg((volatile local unsigned int *)source, prevVal.intVal, newVal.intVal) != prevVal.intVal) 

除去预选赛(为简单起见)和重命名参数后,atomic_cmpxchg签名如下:

int atomic_cmpxchg(int *ptr, int expected, int new) 

它所做的是:

atomically { 
    int old = *ptr; 

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

    return old; 
} 

总之,每个线程,单独做:

  1. 负荷*source从存储器的电流值成preVal.floatVal
  2. newVal.floatVal
  3. *source
  4. 计算所需的值执行上述的原子比较 - 交换(使用型punned值)
  5. 如果结果的atomic_cmpxchg == newVal.intVal,它意味着比较交换是成功的,休息。否则,交换没有发生,请转到1并重试。

上述循环最终终止,因为最终,每个线程成功地做他们的atomic_cmpxchg

我们可以说每个工作项目都有一个互斥体访问(我的意思是一个锁定的访问)到位于源地址的值。

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

+0

当你说“如果atomic_cmpxchg == newVal.intVal的结果,它意味着比较交换成功,break”与“do {} while(atomic_cmpxchg((volatile local local unsigned int *)source,prevVal.intVal, newVal.intVal)!= prevVal.intVal)“,但我认为我们应该写:”do {} while(atomic_cmpxchg((易失性本地无符号整型*)源,prevVal.intVal,newVal.intVal)== prevVal。 intVal)“,因为当atomic_cmpxchg的结果是newVal.intVal(如果”* source == preVal.floatVal“)时,我们打破了while循环,不是吗? – youpilat13

+0

不,'atomic_cmpxchg'返回'* ptr'的* old *值,如上图所示,因此,当它等于'prevVal'时要中断,因为它成功了。如果线程未能进行原子交换,那么您建议的循环会立即退出。 –

+0

我想我已经明白了,您能否在我的上面的UPDATE 1中看到我的解释?谢谢 – youpilat13