Cuda 同步线程与全局内存

Cuda 同步线程与全局内存,cuda,synchronization,global-variables,Cuda,Synchronization,Global Variables,我已经编写了一个使用并行归约的小型内核do sum 2^k元素。 这里没有什么新的…我的向量存储在全局内存中,我将向量的每个部分分配给不同的块,并将每个块减少到单个位置。其余的我在CPU里做 __global__ void sum(real *v, long int s){ long int ix = threadIdx.x; long int shift = blockIdx.x*blockDim.x; long int h = blockDim.x/2;

我已经编写了一个使用并行归约的小型内核do sum 2^k元素。 这里没有什么新的…我的向量存储在全局内存中,我将向量的每个部分分配给不同的块,并将每个块减少到单个位置。其余的我在CPU里做

__global__ void sum(real *v, long int s){

    long int ix     =  threadIdx.x;
    long int shift = blockIdx.x*blockDim.x;

    long int h = blockDim.x/2;
    while (h >= 1){
        if (ix < h){
            v[ix +  shift] = v[2*ix + shift] + v[2*ix + 1 + shift];
        }
        __syncthreads(); 
        h = h / 2;
    }
}
\uuuu全局\uuuuu无效和(实*v,长整数s){
long int ix=threadIdx.x;
long int shift=blockIdx.x*blockDim.x;
长整数h=blockDim.x/2;
而(h>=1){
if(ix
代码是有效的。然而,经过仔细检查,我意识到也许它不应该工作。所以我很困惑。。。。可能是线程_id=1对元素2和3求和,在线程_id=0能够读取元素0和1之前将其和写入位置1。从而使结果无效

我会假设,为了安全起见,代码必须是

__global__ void sumsafe(real *v, long int s){
    long int ix     =  threadIdx.x;
    long int shift = blockIdx.x*blockDim.x;
    real x = 0;
    long int h = blockDim.x/2;
    while (h >= 1){
        if (ix < h){
            x = v[2*ix + shift] + v[2*ix + 1 + shift];
        }
        __syncthreads(); 
        if (ix < h){
            v[ix +  shift] = x;
        }
        __syncthreads();
        h = h / 2;
    }
}
\uuuuu全局\uuuuuuvoid sumsafe(实*v,长整数s){
long int ix=threadIdx.x;
long int shift=blockIdx.x*blockDim.x;
实x=0;
长整数h=blockDim.x/2;
而(h>=1){
if(ix
因此,我保证所有线程在开始更改它们之前都会读取它们的值。但正如我所说……两种代码都有效……它们的时间实际上也差不多

为什么会这样

我知道GPU不能保证一个线程写入全局内存的内容对其他线程不可见。但这也不能保证这种情况永远不会发生

有什么想法吗!?我正在研制GTX 1080。

您真是“幸运”,因为CUDA不保证翘曲的执行顺序。下面的描述(这是猜测)不应该被解释为你所展示的是一个好主意。任何人都不应该这样做

但是对于一个小的测试用例(除了这个,没有其他代码,并且在单个数据块上操作),我希望它能工作

从全局内存读取通常具有很高的延迟。当执行遇到这行代码时:

        v[ix +  shift] = v[2*ix + shift] + v[2*ix + 1 + shift];
这将转化为SASS指令,如下所示:

LD  R0, v[2*ix + shift]        (let's call this LD0)
LD  R1, v[2*ix + 1 + shift];   (let's call this LD1)
ADD R3, R0, R1
ST  v[ix + shift], R3
现在,前两次LD操作不会导致失速。但是,如果R1和R0无效,则添加操作将导致暂停(无法发出)

暂停的结果是SM中的warp调度引擎将寻找其他可用的工作。其他可用工作可能构成上述其他翘曲的代码

由于ADD指令在读取完成之前无法发出,并且由于warp调度程序对暂停的响应,读取(跨warp)都有效地背靠背发出,因此在ADD指令完成发出时,读取操作将趋于全部完成,这意味着在发出所有添加操作之前,所有读取都已完成(并且在相应的添加操作完成之前,无法发出ST)。ADD还有一个管道延迟,因此ADD操作可能也会按顺序发出(但此处较短的管道延迟可能会增加危险的概率),并且在相应的ADD操作完成之前,无法发出给定的ST操作。净影响可能是:

LD0 W0
LD1 W0
LD0 W1
LD1 W1
...   (all LD0 and LD1 get issued across all warps W0..WN)
<read latency stall --  eventually the first 2 LD0 and LD1 complete>
ADD W0
<read pipeline latency - 1 cycle>
ADD W1
<read pipeline latency - 1 cycle>
ADD W2
...
<add pipeline latency>
ST W0
<add pipeline latency>
ST W1
...
LD0 W0
LD1 W0
LD0 W1
LD1 W1
...   (所有LD0和LD1将跨所有扭曲W0..WN发布)
添加W0
添加W1
添加W2
...
\uuu syncthreads()
应该保证迭代
i+1
的读取不会被迭代
i
的写入(未能见证)破坏,因为CUDA不能保证扭曲的执行顺序,所以您确实是“幸运的”。下面的描述(这是猜测)不应该被解释为你所展示的是一个好主意。任何人都不应该这样做

但是对于一个小的测试用例(除了这个,没有其他代码,并且在单个数据块上操作),我希望它能工作

从全局内存读取通常具有很高的延迟。当执行遇到这行代码时:

        v[ix +  shift] = v[2*ix + shift] + v[2*ix + 1 + shift];
这将转化为SASS指令,如下所示:

LD  R0, v[2*ix + shift]        (let's call this LD0)
LD  R1, v[2*ix + 1 + shift];   (let's call this LD1)
ADD R3, R0, R1
ST  v[ix + shift], R3
现在,前两次LD操作不会导致失速。但是,如果R1和R0无效,则添加操作将导致暂停(无法发出)

暂停的结果是SM中的warp调度引擎将寻找其他可用的工作。其他可用工作可能构成上述其他翘曲的代码

由于ADD指令在读取完成之前无法发出,并且由于warp调度程序对暂停的响应,读取(跨warp)都有效地背靠背发出,因此在ADD指令完成发出时,读取操作将趋于全部完成,这意味着在发出所有添加操作之前,所有读取都已完成(并且在相应的添加操作完成之前,无法发出ST)。ADD还有一个管道延迟,因此ADD操作可能也会按顺序发出(但此处较短的管道延迟可能会增加危险的概率),并且在相应的ADD操作完成之前,无法发出给定的ST操作。净影响可能是:

LD0 W0
LD1 W0
LD0 W1
LD1 W1
...   (all LD0 and LD1 get issued across all warps W0..WN)
<read latency stall --  eventually the first 2 LD0 and LD1 complete>
ADD W0
<read pipeline latency - 1 cycle>
ADD W1
<read pipeline latency - 1 cycle>
ADD W2
...
<add pipeline latency>
ST W0
<add pipeline latency>
ST W1
...
LD0 W0
LD1 W0
LD0 W1
LD1 W1
...   (所有LD0和LD1将跨所有扭曲W0..WN发布)
添加W0
添加W1
添加W2
...

如果不使用共享内存,为什么调用
\u syncthreads()
呢?另外,您永远不应该假定线程执行的特定顺序