CUDA 学习记录

1.关于volatile:

对于文章中这个函数,

__global__ void reduceUnrollWarps8 (int *g_idata, int *g_odata, unsigned int n)
{
    // set thread ID
    unsigned int tid = threadIdx.x;
    unsigned int idx = blockIdx.x * blockDim.x * 8 + threadIdx.x;

    // convert global data pointer to the local pointer of this block
    int *idata = g_idata + blockIdx.x * blockDim.x * 8;

    // unrolling 8
    if (idx + 7 * blockDim.x < n)
    {
        int a1 = g_idata[idx];
        int a2 = g_idata[idx + blockDim.x];
        int a3 = g_idata[idx + 2 * blockDim.x];
        int a4 = g_idata[idx + 3 * blockDim.x];
        int b1 = g_idata[idx + 4 * blockDim.x];
        int b2 = g_idata[idx + 5 * blockDim.x];
        int b3 = g_idata[idx + 6 * blockDim.x];
        int b4 = g_idata[idx + 7 * blockDim.x];
        g_idata[idx] = a1 + a2 + a3 + a4 + b1 + b2 + b3 + b4;
    }

    __syncthreads();

    // in-place reduction in global memory
    for (int stride = blockDim.x / 2; stride > 32; stride >>= 1)
    {
        if (tid < stride)
        {
            idata[tid] += idata[tid + stride];
        }

        // synchronize within threadblock
        __syncthreads();
    }

    // unrolling warp
    if (tid < 32)
    {
        volatile int *vmem = idata;
        vmem[tid] += vmem[tid + 32];
        vmem[tid] += vmem[tid + 16];
        vmem[tid] += vmem[tid +  8];
        vmem[tid] += vmem[tid +  4];
        vmem[tid] += vmem[tid +  2];
        vmem[tid] += vmem[tid +  1];
    }

    // write result for this block to global mem
    if (tid == 0) g_odata[blockIdx.x] = idata[0];
}

在  //unrolling wrap 之前的for循环完成后,只剩下idata[0] 到 idata[63](共64个元素)的数据还没有进行加和。

之前的疑问是 最后的if语句块内为什么会完成这个操作而不出问题:

因为一个线程束内的线程会进行同步,所以if里的语句是,所有线程执行第一条语句,都执行完了,再执行第二条语句。

然后关于volitale ,大家都说是禁止编译器优化,我的理解是:

如果没有volatile,各个线程写寄存器的时候是有同步的,从寄存器写到共享内存是没有同步的。(不过在我这里,去掉volatile结果也是对的。。。)

2.关于延迟隐藏

目前个人理解就是让一个线程尽可能多做事。

3.关于如何获得最大线程束 

参考:【精选】CUDA编程:笔记2_线程束_longlongqin的博客-CSDN博客

little法则给出了下面的计算公式"所需线程束 = 延迟 × 吞吐量 

带宽:一般指的是理论峰值,最大每个时钟周期能执行多少个指令;

吞吐量:是指实际操作过程中每分钟处理多少个指令。

每次想到还是看书吧。 

一个WRAP访存周期是T,刚好是Wrap处理指令时间周期t的K倍,只需要K个wrap就能隐藏访存带来的延迟。

得到最大线程束数量还要减少分支

4.选择合适的网格和块:

有个工具包(同学说不能使了,但是应该也提供了别的方法)。

4.什么是事务内存

参考:初识事务内存(Transactional Memory) - 知乎

个人理解:比u锁更好的具有原子性的互斥手段。 

5.在GPU上只有内存加载操作可以被缓存,内存存储操作不能被缓存。

内存加载操作:从全局内存或其他内存读数据加载到寄存器或缓存中。

内存存储操作:将数据从寄存器或缓存写入到内存

线程加载数据时,如果缓存中有数据,可以直接从缓存中加载。而存储操作通常不能被缓存,因为要保证数据的一致性,必须及时更新内存中的数据,而不能依赖于缓存。

6.对于固定内存和零拷贝内存

当传输大数据时,使用固定内存:cudaError_t cudaMallocHost(void **devPtr, size_t count);

cudaError_t cudaFreeHost(void *ptr);

如果想要共享主机和设备端的少量数据,使用零拷贝内存:

cudaErroer_t cudaHostAlloc(void **pHost, size_t count, unsigned int flags);

cudaFreeHost()

使用下列函数获取映射到固定内存的设备指针:

cudaError_t cudaHostGetDevicePointer(void **pDevice, void *pHost, unsigned int flags);

    // part 2: using zerocopy memory for array A and B
    // allocate zerocpy memory
    CHECK(cudaHostAlloc((void **)&h_A, nBytes, cudaHostAllocMapped));
    CHECK(cudaHostAlloc((void **)&h_B, nBytes, cudaHostAllocMapped));

    // initialize data at host side
    initialData(h_A, nElem);
    initialData(h_B, nElem);
    memset(hostRef, 0, nBytes);
    memset(gpuRef,  0, nBytes);

    // pass the pointer to device
    CHECK(cudaHostGetDevicePointer((void **)&d_A, (void *)h_A, 0));
    CHECK(cudaHostGetDevicePointer((void **)&d_B, (void *)h_B, 0));

    // add at host side for result checks
    sumArraysOnHost(h_A, h_B, hostRef, nElem);

    // execute kernel with zero copy memory
    sumArraysZeroCopy<<<grid, block>>>(d_A, d_B, d_C, nElem);

    // copy kernel result back to host side
    CHECK(cudaMemcpy(gpuRef, d_C, nBytes, cudaMemcpyDeviceToHost));

    // check device results
    checkResult(hostRef, gpuRef, nElem);

    // free  memory
    CHECK(cudaFree(d_C));
    CHECK(cudaFreeHost(h_A));
    CHECK(cudaFreeHost(h_B));

有了虚拟内存寻址(UVA)后,由cudaHostAlloc()分配的固定主机内存具有相同的主机和设备指针,可以将返回的指针直接传递给核函数:

cudaHostAlloc((void **)&h_A, nBytes, cudaHostAllocMapped);
cudaHostAlloc((void **)&h_B, nBytes, cudaHostAllocMapped);

initialData(h_A, nElem);
initialData(h_B, nElem);

sumArrayZeroCopy<<<grid, block>>>(h_A, h_B, d_C, nElem);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值