logo资料库

perflab实验报告.doc

第1页 / 共17页
第2页 / 共17页
第3页 / 共17页
第4页 / 共17页
第5页 / 共17页
第6页 / 共17页
第7页 / 共17页
第8页 / 共17页
资料共17页,剩余部分请下载后查看
课程实验报告 课 程 名 称: 实验项目名称: perflab——代码性能优化 计算机组成与结构 专 业 班 级: 姓 学 名: 号: 保密 1301 杜佳卉 201308060206 指 导 教 师: 完 成 时 间: 2015 年 5 月 22 日 计算机科学与工程系
实验题目:perflab——代码性能优化 实验目的:兼顾算法原型的理解,学会以底层的角度对抽象计算作最大限度的优化。 实验环境:个人电脑,ubuntu 实验内容及操作步骤: 1. rotate 优化过程 Naive_rotate 分析 void naive_rotate(int dim, pixel *src, pixel *dst) { int i, j; for (i = 0; i < dimi; i++) for (j = 0; j < dim; j++) dst[RIDX(dim-1-j, i, dim)] = src[RIDX(i, j, dim)]; }这个函数虽然从功能上完成了对一个像素点的 90 度转移,但实际的运行 CPE 却很低,特别在输入维度规模(dim)扩大时,也随之及剧增长。 低效的原因大致为一下几点:程序的局部性差;循环的步进值仅为 1,太小 了,使整个运行的次数较多;每次内循环中都需重复计算源像素及目标像 素的地址。 但反过来,程序的书写风格却是最与算法原理与编写抽象思维最贴近的。就 是说,高级语言经过编译器编译后得到的机器指令往往并不是最高效的,需 要通过适当的转化、变形、再设计达到机器性能的提升。 改进 1: 这是第一个改进版本。考虑到原函数的局部性较低,及循环展开次数多的问题,我将 90 度翻转的像素点作了一个 4*4 块的分割。这样对于 Cache 的命中角度而言有所帮助。 运行后的结果如下:
性能较之前的在大规模输入下有了明显的提高。但还具有进一步提升的可能。 改进 2: 顺着之前的思路,将循环的步长改为 32,作一个 32*32 块的分割,运行的结果 如下: 但之后再进一步改用 64*64 分块的时候遇到了如下的错误。 因此我们可以看到如果要能充分使用到 Cache 时,选择 32*32 的分块决策可能 是最优的。 改进 3:
根据改进 2,可知 Cache 的分块极限情况。而这一次改进回到分块的原点——4*4 分块并尝试新的循环展开方式优化。同时将取地址计算时的相同计算提取到内循环之 外。并用统一的指针进行循环遍历。 这样处理之后的运行效率提升如下: 改进 3 不如改进 2 的提升幅度大,但与改进 1 相比性能有所改善,分析可能的原 因,主要为: a) 这个版本为 4*4 分块,虽然在地址计算及并行展开上有小幅提升,但整体效率仍 被分块化所抵消。(与 32*32 unrolled 方式相比) b) 与改进 1 的 4*4 相比,改善点在于将原来的循环步长加 4,循环展开为了内循环 中的 4 次像素转移,更充分地利用了 CPU 的资源。 因此下一步的目标在于,将程序变为 32*32 分块模式,并用内循环 32 位展开的方式进 行处理。 (时间来不及了没有做)
2. Smooth 优化过程 Naive Smooth 分析 void naive_smooth(int dim, pixel *src, pixel *dst) { int i, j; for (i = 0; i < dim; i++) for (j = 0; j < dim; j++) dst[RIDX(i, j, dim)] = avg(dim, i, j, src); } 经过 rotate 优化的训练后,对于性能优化有了初步的经验。因此在分析第二个 函数时,直接沿用第一个优化结果的分析思路,可知该段程序的效率不足点在于: a) 循环并未分块,如何分块比较有效? b) 函数调用过多。Avg 函数中又包括了其他几个辅助函数。 c) 循环内部并未展开,未利用流水线特性及局部性作 cache 层次的优化 d) 内循环寻址计算重复,并未用指针直接增减。 e) 算法计算并未最优化,Avg 函数中的循环判断套用了大量 max,min 宏定义。 Sum 函数累加的效率很低。 介于这五点,选取部分作优化处理。 改进 1:
此次优化聚焦于两点:减少甚至去除子函数调用;算法计算流程的优化。 对于算法计算流程的优化,我们分析原本的算法公式可知。整个图片的像素柔滑 处理,相当于对每个像素点做了个领域内的平均值化。 待修改版本中的求和函数先暂且不考虑,对于 avg 子函数而言: static pixel avg(int dim, int i, int j, pixel *src) { int ii, jj; pixel_sum sum; pixel current_pixel; initialize_pixel_sum(&sum); for(ii = max(i-1, 0); ii <= min(i+1, dim-1); ii++) for(jj = max(j-1, 0); jj <= min(j+1, dim-1); jj++) accumulate_sum(&sum, src[RIDX(ii, jj, dim)]); assign_sum_to_pixel(¤t_pixel, sum); return current_pixel; } 程序在边界判定上花了很多功夫,像素循环时利用边界判断,避免了顶点、第一 行、第一列及中间区域的不同处理问题。故接下来我们可以考虑如何能够更加斩 草除根低克服这一性能瓶颈,如何通过一种划分手段做到更好的提升。 我们可以将原始图像的四个端点独立出来,因为它们的领域内只有 4 个点。而 剩下的第一行与第一列像素点则在领域内有 6 个像素点。再剩下的中间区域则 为 9 个点。总结而言,可以大致分为三类:中间区域,4 个端点,4 条最外层 的边。针对这 3 种不同的情况,可以分别用三种不同的计算模式进行分析。 这样做的好处不仅解放了边界判断的冗余效率,又可以无需 pixel_sum 函数的 调用。 直接在求取平均值的时候,除以数字 9 、4 、6。 由此便诞生了第一个修改版本,其执行效率如下:
改进 2: 在这个版本中,我们进一步去除了所有使用的子函数,并重写了 sum 取和方法,将待 翻转图像分成了 3 类进行优化。代码如下: void smooth_v2(int dim, pixel *src, pixel *dst) { int i,j; pixel_sum sum; pixel *p_s=src; pixel *p_d=dst; pixel *op; //the 1st row // (0,0) pixel { op=p_s; initialize_pixel_sum(&sum); sum.red += (int) op->red; sum.green += (int) op->green; sum.blue += (int) op->blue; op++; sum.red += (int) op->red; sum.green += (int) op->green; sum.blue += (int) op->blue; op+=dim; sum.red += (int) op->red; sum.green += (int) op->green; sum.blue += (int) op->blue; op--; sum.red += (int) op->red; sum.green += (int) op->green; sum.blue += (int) op->blue; op=p_d; op->red = (unsigned short) (sum.red/4); op->green = (unsigned short) (sum.green/4); op->blue = (unsigned short) (sum.blue/4); p_d++; } //pixels from (1,1) to (1,dim-2) for (j = 1; j red;
sum.green += (int) op->green; sum.blue += (int) op->blue; op++; sum.red += (int) op->red; sum.green += (int) op->green; sum.blue += (int) op->blue; op++; sum.red += (int) op->red; sum.green += (int) op->green; sum.blue += (int) op->blue; op+=dim; sum.red += (int) op->red; sum.green += (int) op->green; sum.blue += (int) op->blue; op--; sum.red += (int) op->red; sum.green += (int) op->green; sum.blue += (int) op->blue; op--; sum.red += (int) op->red; sum.green += (int) op->green; sum.blue += (int) op->blue; op=p_d; op->red = (unsigned short) (sum.red/6); op->green = (unsigned short) (sum.green/6); op->blue = (unsigned short) (sum.blue/6); p_s++; p_d++; } { //pixel (1,dim-1) op=p_s; initialize_pixel_sum(&sum); sum.red += (int) op->red; sum.green += (int) op->green;
分享到:
收藏