GPU编程(四): 并行规约优化

简介: 目录前言cuda-gdb未优化并行规约优化后并行规约结果分析最后前言之前第三篇也看到了, 并行方面GPU真的是无往不利, 现在再看下第二个例子, 并行规约. 通过这次的例子会发现, 需要了解GPU架构, 然后写出与之对应的算法的, 两者结合才能得到令人惊叹的结果.

目录

  • 前言
  • cuda-gdb
  • 未优化并行规约
  • 优化后并行规约
  • 结果分析
  • 最后

前言

  • 之前第三篇也看到了, 并行方面GPU真的是无往不利, 现在再看下第二个例子, 并行规约. 通过这次的例子会发现, 需要了解GPU架构, 然后写出与之对应的算法的, 两者结合才能得到令人惊叹的结果.
  • 这次也会简要介绍下cuda-gdb的用法, 其实和gdb用法几乎一样, 也就是多了个cuda命令.

cuda-gdb

如果之前没有用过gdb, 可以速学一下, 就几个指令.
想要用cuda-gdb对程序进行调试, 首先你要确保你的gpu没有在运行操作系统界面, 比方说, 我用的是ubuntu, 我就需要用sudo service lightdm stop关闭图形界面, 进入tty1这种字符界面.
当然用ssh远程访问也是可以的.
接下来, 使用第二篇中矩阵加法的例子. 但是注意, 编译的使用需要改变一下, 加入-g -G参数, 其实和gdb是相似的.

nvcc -g -G CUDAAdd.cu -o CUDAAdd.o

然后使用cuda-gdb CUDAAdd.o即可对程序进行调试.

cuda-gdb

在调试之前, 我把代码贴出来:

#include <stdio.h>

__global__ void add(float * x, float *y, float * z, int n){
        int index = threadIdx.x + blockIdx.x * blockDim.x;
        int stride = blockDim.x * gridDim.x;

        for (int i = index; i < n; i += stride){
                z[i] = x[i] + y[i];
        }
}

int main()
{
    int N = 1 << 20;
    int nBytes = N * sizeof(float);

    float *x, *y, *z;
    cudaMallocManaged((void**)&x, nBytes);
    cudaMallocManaged((void**)&y, nBytes);
    cudaMallocManaged((void**)&z, nBytes);

    for (int i = 0; i < N; ++i)
    {
        x[i] = 10.0;
        y[i] = 20.0;
    }

    dim3 blockSize(256);
    // 4096
    dim3 gridSize((N + blockSize.x - 1) / blockSize.x);

    add << < gridSize, blockSize >> >(x, y, z, N);

    cudaDeviceSynchronize();

    float maxError = 0.0;
    for (int i = 0; i < N; i++){
                maxError = fmax(maxError, (float)(fabs(z[i] - 30.0)));
    }
    printf ("max default: %.4f\n", maxError);

    cudaFree(x);
    cudaFree(y);
    cudaFree(z);

    return 0;
}

之后就是常规操作了, 添加断点, 运行, 下一步, 查看想看的数据. 不同点是cuda的指令, 例如cuda block(1,0,0)可以从一开始block(0,0,0)切换到block(1,0,0).

cuda-gdb

cuda-gdb


未优化并行规约

如果按照常规的思路, 两两进行进行加法运算. 每次步长翻倍即可, 从算法的角度来说, 这是没啥问题的. 但是没有依照GPU架构进行设计.

未优化并行规约

#include <stdio.h>

const int    threadsPerBlock = 512;
const int    N        = 2048;
const int    blocksPerGrid    = (N + threadsPerBlock - 1) / threadsPerBlock; /* 4 */

__global__ void ReductionSum( float * d_a, float * d_partial_sum )
{
    /* 申请共享内存, 存在于每个block中 */
    __shared__ float partialSum[threadsPerBlock];

    /* 确定索引 */
    int    i    = threadIdx.x + blockIdx.x * blockDim.x;
    int    tid    = threadIdx.x;

    /* 传global memory数据到shared memory */
    partialSum[tid] = d_a[i];

    /* 传输同步 */
    __syncthreads();

    /* 在共享存储器中进行规约 */
    for ( int stride = 1; stride < blockDim.x; stride *= 2 )
    {
        if ( tid % (2 * stride) == 0 )
            partialSum[tid] += partialSum[tid + stride];
        __syncthreads();
    }

    /* 将当前block的计算结果写回输出数组 */
    if ( tid == 0 )
        d_partial_sum[blockIdx.x] = partialSum[0];
}


int main()
{
    int size = sizeof(float);

    /* 分配显存空间 */
    float    * d_a;
    float    * d_partial_sum;

    cudaMallocManaged( (void * *) &d_a, N * size );
    cudaMallocManaged( (void * *) &d_partial_sum, blocksPerGrid * size );

    for ( int i = 0; i < N; ++i )
        d_a[i] = i;

    /* 调用内核函数 */
    ReductionSum << < blocksPerGrid, threadsPerBlock >> > (d_a, d_partial_sum);

    cudaDeviceSynchronize();

    /* 将部分和求和 */
    int sum = 0;
    for ( int i = 0; i < blocksPerGrid; ++i )
        sum += d_partial_sum[i];

    printf( "sum = %d\n", sum );

    /* 释放显存空间 */
    cudaFree( d_a );
    cudaFree( d_partial_sum );

    return(0);
}

优化后并行规约

其实需要改动的地方非常小, 改变步长即可.

优化后并行规约

__global__ void ReductionSum( float * d_a, float * d_partial_sum )
{
    // 相同, 略去
    /* 在共享存储器中进行规约 */
    for ( int stride = blockDim.x / 2; stride > 0; stride /= 2 )
    {
        if ( tid < stride )
            partialSum[tid] += partialSum[tid + stride];
        __syncthreads();
    }
    // 相同, 略去
}

结果分析

之前的文章里面也说过warp.
warp: GPU执行程序时的调度单位, 目前cuda的warp的大小为32, 同在一个warp的线程, 以不同数据资源执行相同的指令, 这就是所谓SIMT.
说人话就是, 这32个线程必须要干相同的事情, 如果有线程动作不一致, 就需要等待一波线程完成自己的工作, 然后再去做另外一件事情.
所以, 用图说话就是, 第二种方案可以更快将warp闲置, 交给GPU调度, 所以, 肯定是第二种更快.

未优化并行规约

优化后并行规约

图一在运算依次之后, 没有warp可以空闲, 而图二直接空闲2个warp. 图一到了第二次可以空闲2个warp, 而图二已经空闲3个warp. 我这副图只是示意图, 如果是实际的, 差距会更大.

所以来看下运行耗时, 会发现差距还是很大的, 几乎是差了一倍. 不过GPU确实算力太猛, 这样看还不太明显, 有意放大数据量会更加明显.

运行结果

最后

所以GPU又一次展示了强大的算力, 而且, 这次也看到了只是小小变动, 让算法更贴合架构, 就让运算耗时减半, 所以在优化方面可以做的工作真的是太多了, 之后还有更多优化相关的文章, 有意见或者建议, 评论区见哦~


相关实践学习
部署Stable Diffusion玩转AI绘画(GPU云服务器)
本实验通过在ECS上从零开始部署Stable Diffusion来进行AI绘画创作,开启AIGC盲盒。
目录
相关文章
|
6月前
|
人工智能 缓存 调度
技术改变AI发展:RDMA能优化吗?GDR性能提升方案(GPU底层技术系列二)
随着人工智能(AI)的迅速发展,越来越多的应用需要巨大的GPU计算资源。GPUDirect RDMA 是 Kepler 级 GPU 和 CUDA 5.0 中引入的一项技术,可以让使用pcie标准的gpu和第三方设备进行直接的数据交换,而不涉及CPU。
135943 6
|
6月前
|
并行计算 TensorFlow 调度
推荐场景GPU优化的探索与实践:CUDA Graph与多流并行的比较与分析
RTP 系统(即 Rank Service),是一个面向搜索和推荐的 ranking 需求,支持多种模型的在线 inference 服务,是阿里智能引擎团队沉淀多年的技术产品。今年,团队在推荐场景的GPU性能优化上又做了新尝试——在RTP上集成了Multi Stream,改变了TensorFlow的单流机制,让多流的执行并行,作为增加GPU并行度的另一种选择。本文详细介绍与比较了CUDA Graph与多流并行这两个方案,以及团队的实践成果与心得。
|
2月前
|
存储 并行计算 算法
CUDA统一内存:简化GPU编程的内存管理
在GPU编程中,内存管理是关键挑战之一。NVIDIA CUDA 6.0引入了统一内存,简化了CPU与GPU之间的数据传输。统一内存允许在单个地址空间内分配可被两者访问的内存,自动迁移数据,从而简化内存管理、提高性能并增强代码可扩展性。本文将详细介绍统一内存的工作原理、优势及其使用方法,帮助开发者更高效地开发CUDA应用程序。
|
4月前
|
并行计算 API 数据处理
GPU(图形处理单元)因其强大的并行计算能力而备受关注。与传统的CPU相比,GPU在处理大规模数据密集型任务时具有显著的优势。
GPU(图形处理单元)因其强大的并行计算能力而备受关注。与传统的CPU相比,GPU在处理大规模数据密集型任务时具有显著的优势。
|
6月前
|
机器学习/深度学习 并行计算 PyTorch
【多GPU炼丹-绝对有用】PyTorch多GPU并行训练:深度解析与实战代码指南
本文介绍了PyTorch中利用多GPU进行深度学习的三种策略:数据并行、模型并行和两者结合。通过`DataParallel`实现数据拆分、模型不拆分,将数据批次在不同GPU上处理;数据不拆分、模型拆分则将模型组件分配到不同GPU,适用于复杂模型;数据和模型都拆分,适合大型模型,使用`DistributedDataParallel`结合`torch.distributed`进行分布式训练。代码示例展示了如何在实践中应用这些策略。
1746 2
【多GPU炼丹-绝对有用】PyTorch多GPU并行训练:深度解析与实战代码指南
|
12月前
|
存储 人工智能 芯片
多GPU训练大型模型:资源分配与优化技巧 | 英伟达将推出面向中国的改良芯片HGX H20、L20 PCIe、L2 PCIe
在人工智能领域,大型模型因其强大的预测能力和泛化性能而备受瞩目。然而,随着模型规模的不断扩大,计算资源和训练时间成为制约其发展的重大挑战。特别是在英伟达禁令之后,中国AI计算行业面临前所未有的困境。为了解决这个问题,英伟达将针对中国市场推出新的AI芯片,以应对美国出口限制。本文将探讨如何在多个GPU上训练大型模型,并分析英伟达禁令对中国AI计算行业的影响。
1490 0
|
6月前
|
机器学习/深度学习 人工智能 弹性计算
阿里云林立翔:基于阿里云GPU的AIGC小规模训练优化方案
阿里云弹性计算林立翔在【AIGC】话题下带来了题为《基于阿里云GPU的AIGC小规模训练优化方案》的主题演讲,围绕生成式AI技术栈、生成式AI微调训练和性能分析、ECS GPU实例为生成式AI提供算力保障、应用场景案例等相关话题展开。
|
6月前
|
缓存 并行计算 算法
上帝视角看GPU(5):图形流水线里的不可编程单元
上帝视角看GPU(5):图形流水线里的不可编程单元
156 0
|
6月前
|
存储 人工智能 Kubernetes
阿里云ACK助力GPU成本优化,实现灵活管理
摘要:本文将介绍如何在阿里云容器服务ACK中,利用cGPU技术,共享GPU算力,提高GPU利用率,降低TCO。
156 2
|
6月前
|
机器学习/深度学习 并行计算 流计算
【GPU】GPU CUDA 编程的基本原理是什么?
【GPU】GPU CUDA 编程的基本原理是什么?
158 0