【问题标题】:Optimization through loop unrolling and blocking通过循环展开和阻塞进行优化
【发布时间】:2017-12-26 20:39:52
【问题描述】:

我不确定我还能如何优化这段代码以使其高效。到目前为止,我已经将内部 for 循环相对于 j 展开了 16 倍,它产生的平均 CPE 为 1.4。我需要通过优化技术获得大约 2.5 的平均 CPE。我已经阅读了其他可用的问题,与我的问题所涉及的代码相比,它们有点不同。代码的第一部分显示了我得到的内容,然后是我尝试展开循环。给定的代码将扫描源图像矩阵的行并复制到目标图像矩阵的翻转行。任何帮助将不胜感激!

RIDX 宏:

#define RIDX(i,j,n) ((i)*(n)+(j))

给定:

 void naive_rotate(int dim, struct pixel_t *src, struct pixel_t *dst)
 {
 int i, j;
 for(i = 0; i < dim; i++)
     {
             for(j = 0; j < dim; j++)
             {
                dst[RIDX(dim-1-i, j, dim)] = src[RIDX(i, j, dim)];
            }
    }
}

我的尝试:这确实对其进行了优化,但随着平均 CPE 从 1.0 上升到 1.4,它只是稍微优化了一点。我希望它在 2.5 左右,并且我尝试了各种类型的阻塞和我在网上阅读过的东西,但没有设法对其进行更多优化。

 for(i = 0; i < dim; i++){                                                                                                                                                  
 for(j = 0; j < dim; j+=16){                                                                                                                                                      

  dst[RIDX(dim-1-i,j, dim)] = src[RIDX(i,j,dim)];                                                                                                                                
  dst[RIDX(dim-1-i,j+1, dim)] = src[RIDX(i,j+1,dim)];                                                                                                                            
  dst[RIDX(dim-1-i,j+2, dim)] = src[RIDX(i,j+2,dim)];                                                                                                                            
  dst[RIDX(dim-1-i,j+3, dim)] = src[RIDX(i,j+3,dim)];                                                                                                                            
  dst[RIDX(dim-1-i,j+4, dim)] = src[RIDX(i,j+4,dim)];                                                                                                                            
  dst[RIDX(dim-1-i,j+5, dim)] = src[RIDX(i,j+5,dim)];                                                                                                                            
  dst[RIDX(dim-1-i,j+6, dim)] = src[RIDX(i,j+6,dim)];                                                                                                                            
  dst[RIDX(dim-1-i,j+7, dim)] = src[RIDX(i,j+7,dim)];                                                                                                                            
  dst[RIDX(dim-1-i,j+8, dim)] = src[RIDX(i,j+8,dim)];                                                                                                                            
  dst[RIDX(dim-1-i,j+9, dim)] = src[RIDX(i,j+9,dim)];                                                                                                                            
  dst[RIDX(dim-1-i,j+10, dim)] = src[RIDX(i,j+10,dim)];                                                                                                                          
  dst[RIDX(dim-1-i,j+11, dim)] = src[RIDX(i,j+11,dim)];                                                                                                                          
  dst[RIDX(dim-1-i,j+12, dim)] = src[RIDX(i,j+12,dim)];                                                                                                                          
  dst[RIDX(dim-1-i,j+13, dim)] = src[RIDX(i,j+13,dim)];                                                                                                                          
  dst[RIDX(dim-1-i,j+14, dim)] = src[RIDX(i,j+14,dim)];                                                                                                                          
  dst[RIDX(dim-1-i,j+15, dim)] = src[RIDX(i,j+15,dim)];   

【问题讨论】:

  • 我们可以假设您已经在编译器中提高了优化级别,看看它可以为您做什么?如果没有,我建议从那里开始。
  • 不,我没有这样做。我不完全了解它是如何工作的。我应该通过阻塞、展开和运动等常用技术对其进行优化。
  • RIDX 宏中有什么?了解这一点很重要。
  • 只有当 dim 是 16 的倍数时,循环展开才有效... :(
  • 通过尝试仅安排指针操作,我很快就使用 memcpy 获得了相当好的结果 - 速度极快的 bioost。使用 memcpy 复制/移动 pixel_t 是否安全?可以展示一下吗?

标签: c++ optimization


【解决方案1】:

这是一个快速的老式 memcpy 优化。这通常非常有效。不需要展开。

来自 RIDX:

#define RIDX(i,j,n) ((i)*(n)+(j))

我们知道递增 'j' 组件会转化为简单的指针递增。

struct pixel_t* s = src[RIDX(0, 0, dim)];
struct pixel_t* d = dst[RIDX[dim - 1, 0, dim];

for (int i = 0; i < dim; ++i, d -= (2 * dim))
{
   for (int j = 0; j < dim; ++j, ++s, ++d)
   {
       //dst[RIDX(dim-1-i, j, dim)] = src[RIDX(i, j, dim)];
       *d = *s;  

       // you could do it the hard way and start loop unrolling from here
   }
}

在上面代码的内部循环中,++s, ++d 提示可以进行 memcpy 优化。请注意,只有当我们正在复制的类型可以安全移动时,才可能进行 memcpy 优化。大多数类型都是。但这是必须考虑的事情。使用 memcpy 确实有点违反 c++ 的严格规则,但 memcpy 很快。

然后循环变成:

struct pixel_t* s = src[RIDX(0, 0, dim)];
struct pixel_t* d = dst[RIDX[dim - 1, 0, dim];

for (int i = 0; i < dim; ++i, d -= dim, s += dim)
{
   memcpy(d, s, dim * sizeof(pixel_t));
   // or...
   std::copy(s, s + dim, d);  // which is 'safer' but could be slower...
}

在大多数现代 stl 实现中,std::copy 在大多数情况下将转换为 memcpy。 memcpy 使用书中的所有技巧来加快复制速度 - 循环展开、缓存前瞻等...

【讨论】:

  • 几件事,第一部分基本上是什么?您只是向我展示递增 j 组件的作用还是我需要插入该代码并使用它?在最后一部分中,pixel_ts 和 pixel_td 的声明给我一个错误“不兼容的类型”,你确定这应该有效吗?
  • 我不能使用 memcpy,因为它是不允许的。不允许使用这样的功能。所以这不是一个有效的解决方案,但感谢 Michael 教我有关 memcpy 的知识。
  • 对不起,我生活在现实世界中。这就是它的完成方式。我知道向你的老师解释是不可能的,但不让你使用语言功能并不能教给你任何东西。请原谅我没有完成你的任务。
  • 所以除了使用 memcpy 之外我无法获得任何指导?
  • 您仍然可以使用第一个循环作为阻塞和循环展开的良好基础,只使用简单的指针添加。在这种情况下展开 4 会给你带来很好的结果,或者,你可以实现自己的 memcpy 类函数。
猜你喜欢
  • 2018-11-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多