CUDA-APOD Strong-Scaling Weak-Scaling

读书笔记来自这里

APOD

  • APOD 表示Assess,Parallelize, Optimize, Deploy。是Nvidia官方提出的CUDA应用程序的周期性设计模式,目的很明确,让开发者快速找到程序中可以并行的部分,尽可能地加速计算。

    1. Assess 作为循环设计的入口,评估程序中最耗时的代码部分。

    2. Parallelize 根据原始代码,调用现有的GPU优化库(如cuBLAScuFFTThrust),也可以简单地添加一些预处理器指令作为并行化编译器的提示。

    3. Optimize 有很多方法,可以从很多角度,比如 overlapping data transfers, fine-tuning floating-point operation sequences 等等。这一步一定要用可用的优化工具。

    4. Deploy 经过初步优化后,保证正确性,在有限时间内得到一个较好的结果。而不是将所有可能优化都实现。

      根据具体任务和产品,迭代4步骤。尽可能的得到好的性能提升。

  • 建立优化优先级

    在执行优化前要建立优化优先级。

    高优化优先级是一些优化,这些优化对于大部分CUDA程序而言可以显著提升性能。低优化优先级是一些小的优化,这些优化可能只适用于某些特定的情况下。

    先处理高优先级的优化,后解决低优先级的优化。这保证了在有限时间内提供足有的结果,并且避免了过早优化(premature optimization)。

    常见高优先级:

    1. 用profiler工具找到最耗时的部分
    2. 并行处理可以并行的串行部分

Host 和 Device 的不同

同时含有CPU和GPU的计算系统称作是异构计算系统(Heterogeneous Computing)。为了有效是哟个CUDA,有必要知道Host和Device 的不同。

  • 线程资源

    Host上的流水线可以支持有限数量的并发线程。比如,具有2个32核芯处理器的服务器只能同时运行64个线程(如果CPU支持同时多线程,那么可同时运行的线程数为64的倍数,如128,192)。

    相比之下,CUDA设备上最基本的并行执行单元包含32个线程(一个warp)。现代NVIDIA GPU可以在多处理器上同时支持最多2048个活动线程。如果一个GPU上有80个多处理器(SM),这表示可以有超过160000个并发活动线程。

  • 线程本身

    CPU上的线程通常是重量级实体。操作系统必须用其他线程交换CPU执行通道上的线程,以提供多线程功能。因此,上下文切换(当两个线程被交换时)是缓慢而昂贵的。

    相比之下,GPU上的线程是轻量级。在一个典型的系统中,数千个线程排队等待工作。如果GPU要等待一个warp,它只需在另一个warp上开始执行工作。因为单独的寄存器被分配给所有活动线程,所以在GPU线程之间切换时不需要交换寄存器或其他状态。资源一直分配给每个线程,直到它完成执行。

    简而言之,CPU内核目的是最小化每次一小部分线程的等待时间,而GPU则被设计成处理大量并发、轻量级线程以最大化吞吐量。

  • RAM

    Host由CPU和系统内存构成,Device由GPU和显卡上的存储器构成。所以说,Host和Device有各自的RAM。

这些是硬件层面就并行角度看的不同。总之在这样的异构系统中Host做串行工作,Device做并行工作。

Profiling

目的是找到程序中最耗时函数。有很多Profiler工具,比如gprof

1
2
3
4
5
6
7
8
9
10
11
12
13
$ gcc -O2 -g -pg myprog.c
$ gprof ./a.out > profile.txt
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls ms/call ms/call name
33.34 0.02 0.02 7208 0.00 0.00 genTimeStep
16.67 0.03 0.01 240 0.04 0.12 calcStats
16.67 0.04 0.01 8 1.25 1.25 calcSummaryData
16.67 0.05 0.01 7 1.43 1.43 write
16.67 0.06 0.01 mcount
0.00 0.06 0.00 236 0.00 0.00 tzset
0.00 0.06 0.00 192 0.00 0.00 tolower
...

其中genTimeStep函数是最耗时的。所以是我们优化对象

Strong Scaling & Weak Scaling

这是两类不同的问题,还有两者的混合问题。

了解这里的目的是啥,是为我们的加速设置一个期望值,并且计划一个增强并行化的策略。

  1. Strong scaling 表示待解决的问题总体大小是固定的,当使用更多的处理单元时,解决该问题的时间会相应地减少。

    对应的测量加速公式是Amdahl's Law

  2. Weak scaling 表示每个处理单元(处理器)内所解决的问题大小是固定的,随着处理器数量的增加,问题的总量也会变大。

    对应测量加速的公式是Gustafson's Law

公式理解看这里

得到正确结果是所有计算的目的

一个并行系统可能遇到的关于结果正确性的问题,这些问题在一个串行系统中是不存在的:线程问题,浮点计算带来的问题,CPU和GPU的不同计算方式带来的问题。