专栏名称: GiantPandaLLM
专注于机器学习、深度学习、计算机视觉、图像处理等多个方向技术分享。团队由一群热爱技术且热衷于分享的小伙伴组成。我们坚持原创,每天一到两篇原创技术分享。希望在传播知识、分享知识的同时能够启发你,大家一起共同进步(・ω<)☆
目录
相关文章推荐
51好读  ›  专栏  ›  GiantPandaLLM

【博客翻译】让前缀和变得更快

GiantPandaLLM  · 公众号  · 3D  · 2025-05-05 23:24

正文

请到「今天看啥」查看全文


numBlocks = (numElements + threadsPerBlock - 1 ) / threadsPerBlock;
kernel_0
<< >>(input, output);
}

该算法实现了上图所示的功能。在每个阶段,我们将偏移量增加两倍。我们会一直这样做,直到到达线程块的中间位置,并且累积元素之间的距离达到线程块大小的一半。注意,我们需要使用 __syncthreads 来避免竞争条件。如果没有这两个同步屏障,可能会出现两个数组元素同时被读写的情况。

使用一个简单的CPU实现来检查程序的正确性是一个很好的做法。

emplate <int threadsPerBlock, int numElements>
void cpu_scan(int *input, int *output) {
  output[0] = input[0];
  for (int i = 1; i < numElements; i++) {
    if (!((i & (threadsPerBlock - 1)) == 0)) {
      output[i] = input[i] + output[i - 1];
    } else {
      output[i] = input[i];
    }
  }
}

该算法给出了正确的结果。不幸的是,它的性能并不理想。这是由于频繁访问全局内存。我们可以通过计算带宽来衡量性能。我们进行了N次读写操作,其中 N = 1 << 30 = 2**30 。上述kernel的测量性能如下:

Bandwidth: 823.944 GB/s
Efficiency: 0.24968

使用共享内存

template <int threadsPerBlock, int numElements>
__global__ void kernel_1(int *input, int *output) {
extern __shared__ int buffer[threadsPerBlock];

constint tid = threadIdx.x;
constint gtid = blockIdx.x * threadsPerBlock + tid;

  buffer[tid] = input[gtid];
  __syncthreads();

#pragma unroll
for (unsignedint offset = 1; offset <= threadsPerBlock / 2; offset <<= 1) {
    int tmp;
    if (tid >= offset) {
      tmp = buffer[tid - offset];
    }
    __syncthreads();

    if (tid >= offset && gtid < numElements) {
      buffer[tid] += tmp;
    }
    __syncthreads();
  }

if (gtid < numElements) {
    output[gtid] = buffer[tid];
  }
}

template <int threadsPerBlock, int numElements>
void launch_kernel_1(int *input, int






请到「今天看啥」查看全文