↓ 跳过正文
  1. Posts/

优化学习笔记(1):Loop unrolling

·783 字·2 分钟

迫于生计,最近要学习halide

先去学习/复习一下常见的编译优化技巧。

loop unrolling,也就是循环展开,顾名思义,就是把循环展开来写。

 1normal loop:
 2int x;
 3 for (x = 0; x < 100; x++)
 4 {
 5     delete(x);
 6 }
 7
 8after loop unrolling:
 9int x;
10 for (x = 0; x < 100; x += 5 )
11 {
12     delete(x);
13     delete(x + 1);
14     delete(x + 2);
15     delete(x + 3);
16     delete(x + 4);
17 }

循环展开是一种优化,可以手动实现也可以编译器自动实现。

为什么要将循环展开?
#

  * 循环每次都需要判断终止条件,展开后可以消除这部分开销。
  * 减少[分支预测](https://en.wikipedia.org/wiki/Branch_predictor)开销。循环里的分支是指“跳出循环”还是“进行下一次迭代”
  * [vectorization](https://en.wikipedia.org/wiki/Automatic_vectorization)
 1for (int y = 0; y < 4; y++) {
 2            for (int x_outer = 0; x_outer < 2; x_outer++) {
 3                // The loop over x_inner has gone away, and has been
 4                // replaced by a vectorized version of the
 5                // expression. On x86 processors, Halide generates SSE
 6                // for all of this.
 7                int x_vec[] = {x_outer * 4 + 0,
 8                               x_outer * 4 + 1,
 9                               x_outer * 4 + 2,
10                               x_outer * 4 + 3};
11                int val[] = {x_vec[0] + y,
12                             x_vec[1] + y,
13                             x_vec[2] + y,
14                             x_vec[3] + y};
15                printf("Evaluating at <%d, %d, %d, %d>, <%d, %d, %d, %d>:"
16                       " <%d, %d, %d, %d>\n",
17                       x_vec[0], x_vec[1], x_vec[2], x_vec[3],
18                       y, y, y, y,
19                       val[0], val[1], val[2], val[3]);
20            }
21        }

可以看到最里面一层循环被展开以实现向量化.向量化是一种优化计算的手段。该优化的实现基于SIMD 和Advanced_Vector_Extensions(AVX)指令集架构的支持。 * 消除和loop counter(i)有关的除法计算。

1     for (int fused = 0; fused < 4*4; fused++) {
2            int y = fused / 4;
3            int x = fused % 4;
4            printf("Evaluating at x = %d, y = %d: %d\n", x, y, x + y);
5        }
  * 消除循环内部的分支。比如loop counter奇数和偶数时会进入不同的分支,那么将循环展开后,就消除了该分支。

有什么缺点?
#

  * 代码体积增加了。这对于嵌入式设备等往往是不可接受的。
  * 代码可读性变差了。
  * 单一指令可能会使用更多的寄存器,导致性能下降。
  * 如果循环内部包含函数调用,那么和函数的内联(inline)优化会有冲突。原因是,循环展开+函数展开...代码的体积会爆炸。

参考资料
#

  * [Optimizing subroutines in assembly language An optimization guide for x86 platforms(chapter 12.12)](https://www.agner.org/optimize/optimizing_assembly.pdf)
  * [Loop unrolling](https://en.wikipedia.org/wiki/Loop_unrolling)

相关文章

CUDA C Best Practices Guide 阅读笔记(1) 并行计算方法论(APOD)

·759 字·2 分钟
APOD指的是Assess, Parallelize, Optimize, Deploy 如图所示,APOD过程是一个循环的过程,每次只进行一部分,从A到P到O到D,然后再进行下一轮的APOD Assess # 对于一个项目,第一步要做的是接触(Assess)项目,得到项目代码中每部分的执行时间。有了这部分内容,开发者就可以找到并行优化的瓶颈所在,并开始尝试用GPU加速。

cuda error checking 学习笔记

·283 字·1 分钟
由于发现cuda c++ 的 debug方式和c++ 差别很大,因此打算再开一篇,专门记录一些和error checking 以及debug有关的内容. Error checks in CUDA code can help catch CUDA errors at their source. There are 2 sources of errors in CUDA source code:

cuda 学习笔记

·2297 字·5 分钟
update:有毒吧,kernel 中出问题原来不会报错。 请教了组里的 hust 学长 orz.. 学到了 cuda-memcheck 命令和 cudaGetLastError 来查看问题,可以参考What is the canonical way to check for errors using the CUDA runtime API? 先放一波资料。 An Even Easier Introduction to CUDA CUDA C/C++ Basics nvidia-thrust 官方文档 how-access-global-memory-efficiently-cuda-c-kernels efficient-matrix-transpose-cuda-cc 很强大的warp内shuffle cuda-GDB官方文档 cuda-c-best-practices-guide cuda 提出的目的是能够让程序员透明地使用GPU来高效地进行并行运算。