【问题标题】:C Code Optimization, smooth functionC代码优化,平滑功能
【发布时间】:2014-08-08 06:29:36
【问题描述】:

对于一个作业,我们被要求优化代码以实现如下所述的“平滑”功能:

平滑函数将源图像 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; }

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

有人知道我可以如何优化它吗?

【问题讨论】:

  • 首先,实现算法的工作

标签: c optimization


【解决方案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  ][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


说了这么多,值得注意的是你的编译器应该自己做这个。

【讨论】:

  • 不是循环平铺,至少在当今的标准编译器中效率不高。
  • @igon,好的,gcc 有选项-floop-block-floop-strip-mine 以及优化参数--param loop-block-tile-size=N,虽然我不能说它在实际程序中的效果如何。跨度>
  • 嗯,是的,但是如果你看那个人说:“要使用这个代码转换,GCC 必须配置 '--with-ppl' 和 '--with-cloog'启用石墨循环转换基础设施。”这是一个实验性插件,默认禁用多面体变换,因为没有启发式方法来确定何时有益。
  • @igon,确实谢谢。我通常在我构建的编译器中启用它,我从来没有想过发行版默认禁用它:)
【解决方案2】:

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

【讨论】:

    【解决方案3】:

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

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

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

    【讨论】:

      猜你喜欢
      • 2017-03-01
      • 2021-05-27
      • 1970-01-01
      • 2012-11-13
      • 1970-01-01
      • 2013-01-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多