【问题标题】:Understanding the method for OpenCL reduction on float了解 OpenCL 在 float 上减少的方法
【发布时间】:2017-02-02 13:56:04
【问题描述】:

this link之后,我尝试了解内核代码的操作(这个内核代码有两个版本,一个是volatile local float *source,另一个是volatile global float *source,即localglobal版本) .下面我取local版本:

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”,每个工作项共享对source 变量的访问权限,不是吗?

之后,如果我接受一个工作项,代码会将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:

对不起,我几乎了解所有的细微之处,尤其是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 中的条件为真,我们一直在这个循环中,直到不再检查先前的条件。

我的解释是正确的?

谢谢

【问题讨论】:

  • 对不起,如果这对你来说很明显(我想我还没有完全理解这个问题),但是......while 循环是实现原子性的标准方法,截至en.wikipedia.org/wiki/Compare-and-swap
  • :Marco13, :Aldwin 好的,谢谢。让我们看一个有 2 个线程的简单案例。如果第一个在 while 循环中,那么,直到第二个修改“prevVal.floatVal”的值,while 循环才会持续到第一个线程,不是吗?但在这种情况下,递增操作“prevVal.floatVal + 操作数;”是无限的(直到第二个线程停止它),所以存储在地址“源”的值非常高,因为我用大量的“操作数”值求和。问候

标签: opencl atomic


【解决方案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 中计算*source 的所需值
  3. 执行上述原子比较交换(使用类型双关值)
  4. 如果结果为atomic_cmpxchg == newVal.intVal,则表示比较交换成功,break。否则,交换没有发生,请转到 1 并重试。

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

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

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

【讨论】:

  • 当你说“如果 atomic_cmpxchg == newVal.intVal 的结果,这意味着比较交换成功,中断”与“do { } while(atomic_cmpxchg((volatile local unsigned int *) source, prevVal.intVal, newVal.intVal) != prevVal.intVal)”,但我认为我们应该改为:“do { } while(atomic_cmpxchg((volatile local unsigned int *)source, prevVal.intVal, newVal. intVal) == prevVal.intVal)" 因为我们在 atomic_cmpxchg 的结果为 newVal.intVal if "*source == preVal.floatVal" 时中断了 while 循环,不是吗?
  • 我想我已经明白了,你能看看我在上面的更新 1 中的解释吗?谢谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-03
  • 2019-09-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多