C代码优化,功能流畅

时间:2012-12-05 20:50:07

标签: c optimization

对于作业,我们被要求优化代码以获得“平滑”功能,描述如下:

平滑功能将源图像src作为输入,并将平滑后的结果返回到目标图像dst中。以下是实施的一部分:

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); /* Smooth the (i,j)th pixel */
  return; }

struct pixel存储红色,绿色和蓝色值(整数)。 函数avg返回第(i,j)像素周围的所有像素的平均值。您的任务是优化平滑(和平均)以尽可能快地运行。 (注意:函数avg是一个局部函数,你可以完全摆脱它以其他方式实现平滑。) 此代码(以及avg的实现)位于kernels.c。

文件中

任何人都知道如何优化这一点?

3 个答案:

答案 0 :(得分:4)

您可以通过将矩阵/图像划分为方形切片并一次平滑一个切片来执行循环切片/循环条带挖掘。这样可以提高缓存利用率。

考虑当前版本。它遍历图像,一次访问三行,写入中间一行。

a[i-1][0], a[i-1][1], ..., a[i-1][dim-1]
a[i  ][0], a[i  ][1], ..., a[i  ][dim-1]
a[i+1][0], a[i+1][1], ..., a[i+1][dim-1]

当它到达图像的最右侧时,第一列可能会从缓存中丢弃。但是当你移动到下一行时,它们将很快被需要,访问将是:

a[i  ][0], a[i  ][1], ..., a[i  ][dim-1]
a[i+1][0], a[i+1][1], ..., a[i+1][dim-1]
a[i+2][0], a[i+2][1], ..., a[i+2][dim-1]

相反,您可以在图块中处理图像,例如:

a[i  ][B], a[i  ][B+1], ..., a[i  ][B+B-1]
a[i+1][B], a[i+1][B+1], ..., a[i+1][B+B-1]
a[i+2][B], a[i+2][B+1], ..., a[i+2][B+B-1]

其中B是图块大小。

或者用图片,使其更清晰:

000111222
000111222
000111222
333444555
333444555
333444555
666777888
666777888
666777888

这里我们有一个9x9图像,分为9个图块,编号从0到8,你的目标是以这样的方式编写循环:首先平滑图块0中的所有像素,然后是图块1中的所有像素,然后是图块2中的所有像素,等等。顺序并不重要,您甚至可以并行运行每个图块。

当然,这对于大图像和相对较大的图块都是有利的,您可以尝试使用图块大小,例如,从跨越一个或两个缓存行的图块行开始。

有关此方法的更多信息,请查看Loop tiling


尽管如此,值得注意的是你的编译器本身应该这样做。

答案 1 :(得分:2)

根据编译器的优化可提供的内容,这通常会受益于标准优化,例如循环展开,显式矢量化,循环阻塞,以及可能的循环交换,具体取决于图像布局的方向。这些都应该包含在您的教科书或课程笔记中。如果没有,那些就是在线搜索的关键词。

答案 2 :(得分:1)

图像平滑是结构化网格应用程序的常见示例:Structured Grids

您的应用程序肯定会受益于循环展开和循环重新排序技术(尤其是循环平铺),您可以在此处学习:Optimizations

请注意,有效地优化结构化网格计算,尤其是在单个时间步骤上,这并不是一件轻而易举的事情,人们可以获得博士学位:Stencil probe 无论如何,您的计算相当容易,因此您应该实现显着的加速。 但是,实现循环切片可能很麻烦,并且在某些情况下会产生反作用,您可能需要尝试使用多面体编译器,例如Pluto,它能够快速生成具有任意切片尺寸的平铺代码。 选择正确的图块尺寸是获得良好性能的基础,在当前架构中,由于硬件预取矩形图块的存在效果更好:Cache optimizations