【问题标题】:Loop Unrolling, Performance Lab循环展开,性能实验室
【发布时间】:2017-11-10 03:47:38
【问题描述】:

我正在尝试通过循环展开来优化这段代码,

void naive_flip(int dim, pixel *src, pixel *dst) 
{
    int i, j;
    for (i = 0; i < dim; i++){
        for (j = 0; j < dim; j++){
            dst[RIDX_F(i, j, dim)].red   = src[RIDX(i, j, dim)].red;
            dst[RIDX_F(i, j, dim)].green = src[RIDX(i, j, dim)].green;
            dst[RIDX_F(i, j, dim)].blue  = src[RIDX(i, j, dim)].blue;
        }
    }
}

不过,我之前没有真正做过,所以当我尝试时,我得到了这个

void flip_one(int dim, pixel *src, pixel *dst)
{
    //i will be attempting loop unrolling to optimize code
    int i, j;
    for (i=0; i<dim; i+=32)
    {
        for (int j=0; j<dim; j+=32)
        {
            dst[RIDX_F(i, j, dim)].red   = src[RIDX(i, j, dim)].red;
            dst[RIDX_F(i, j+1, dim)].green = src[RIDX(i, j, dim)].green;
            dst[RIDX_F(i, j+2, dim)].blue  = src[RIDX(i, j, dim)].blue;   
        }
        for (int j=0; j<dim; j+=32)
        {
            dst[RIDX_F(i+1, j, dim)].red   = src[RIDX(i, j, dim)].red;
            dst[RIDX_F(i+1, j+1, dim)].green = src[RIDX(i, j, dim)].green;
            dst[RIDX_F(i+1, j+2, dim)].blue  = src[RIDX(i, j, dim)].blue;   
        }
        for (int j=0; j<dim; j+=32)
        {
            dst[RIDX_F(i+2, j, dim)].red   = src[RIDX(i, j, dim)].red;
            dst[RIDX_F(i+2, j+1, dim)].green = src[RIDX(i, j, dim)].green;
            dst[RIDX_F(i+2, j+2, dim)].blue  = src[RIDX(i, j, dim)].blue;   
        }
        for (int j=0; j<dim; j+=32)
        {
            dst[RIDX_F(i+3, j, dim)].red   = src[RIDX(i, j, dim)].red;
            dst[RIDX_F(i+3, j+1, dim)].green = src[RIDX(i, j, dim)].green;
            dst[RIDX_F(i+3, j+2, dim)].blue  = src[RIDX(i, j, dim)].blue;   
        }
    }
}

运行代码时,它不起作用,它给我这个错误:

"错误:Dimension=96, 9216 错误

例如,以下两个像素应该具有相等的值:

src[9215].{红、绿、蓝} = {22543,1426,53562}

dst[9120].{red,green,blue} = {0,0,0}"

感谢任何关于我做错了什么或我应该做的事情的帮助

编辑 我用这个更新了我的代码

void flip_one(int dim, pixel *src, pixel *dst)
{
    //i will be attempting loop unrolling to optimize code
    int i, j;
    for (i=0; i<dim; i++)
    {
        for (int j=0; j<dim; j++)
        {
            dst[RIDX_F(i, j, dim)].red   = src[RIDX(i, j, dim)].red;
            dst[RIDX_F(i, j, dim)].green = src[RIDX(i, j, dim)].green;
            dst[RIDX_F(i, j, dim)].blue  = src[RIDX(i, j, dim)].blue;

            dst[RIDX_F(i, j, dim)].red   = src[RIDX(i, j, dim)].red;
            dst[RIDX_F(i, j, dim)].green = src[RIDX(i, j, dim)].green;
            dst[RIDX_F(i, j, dim)].blue  = src[RIDX(i, j, dim)].blue;

            dst[RIDX_F(i, j, dim)].red   = src[RIDX(i, j, dim)].red;
            dst[RIDX_F(i, j, dim)].green = src[RIDX(i, j, dim)].green;
            dst[RIDX_F(i, j, dim)].blue  = src[RIDX(i, j, dim)].blue;

            dst[RIDX_F(i, j, dim)].red   = src[RIDX(i, j, dim)].red;
            dst[RIDX_F(i, j, dim)].green = src[RIDX(i, j, dim)].green;
            dst[RIDX_F(i, j, dim)].blue  = src[RIDX(i, j, dim)].blue;
        }
    }
}

我不再收到错误(耶!)但这实际上并没有加快它,实际上它减慢了它。也许我做错了什么,但是,我不知道是什么。

编辑 我更新了代码看起来像

void flip_one(int dim, pixel *src, pixel *dst)
{
    //i will be attempting loop unrolling to optimize code
    int i, j;
    for (i=0; i<dim; i++)
    {
        for (int j=0; j<dim; j+=4)
        {
            dst[RIDX_F(i, j, dim)].red   = src[RIDX(i, j, dim)].red;
            dst[RIDX_F(i, j, dim)].green = src[RIDX(i, j, dim)].green;
            dst[RIDX_F(i, j, dim)].blue  = src[RIDX(i, j, dim)].blue;

            dst[RIDX_F(i, j+1, dim)].red   = src[RIDX(i, j+1, dim)].red;
            dst[RIDX_F(i, j+1, dim)].green = src[RIDX(i, j+1, dim)].green;
            dst[RIDX_F(i, j+1, dim)].blue  = src[RIDX(i, j+1, dim)].blue;

            dst[RIDX_F(i, j+2, dim)].red   = src[RIDX(i, j+2, dim)].red;
            dst[RIDX_F(i, j+2, dim)].green = src[RIDX(i, j+2, dim)].green;
            dst[RIDX_F(i, j+2, dim)].blue  = src[RIDX(i, j+2, dim)].blue;

            dst[RIDX_F(i, j+3, dim)].red   = src[RIDX(i, j+3, dim)].red;
            dst[RIDX_F(i, j+3, dim)].green = src[RIDX(i, j+3, dim)].green;
            dst[RIDX_F(i, j+3, dim)].blue  = src[RIDX(i, j+3, dim)].blue;
        }
    }
}

【问题讨论】:

  • 这甚至不是正确的展开 - 你应该重复内部循环中的语句......而且它应该是 compiler 应该为你做的跨度>
  • @Antii 你能给我解释一下吗?就像我说的,我以前从未真正做过。我虽然在重复内部循环(通过我发现的另一篇文章进行了小改动)。是不是应该完全一样。我不明白我做错了什么,因此我寻求帮助
  • 不,只有内循环,也就是说,你应该有 3 * n 个连续的 dst[something] = src[something] 而不干预 for 语句
  • 看看Loop unrolling optimization, how does this work 以及维基百科的文章。
  • 什么是RIDX_F?是宏吗?你能缓存对RIDX_F(i, j, dim)]RIDX(i, j, dim) 的调用吗?如果这是您经常执行的操作,稍微不同的优化是重新排列像素数组,使每个维度的像素是连续的,从而允许您使用memcpy

标签: c optimization loop-unrolling


【解决方案1】:

循环展开的基本思想是在循环体中多次显式地编写计算,而不是让编译器根据循环边界和条件来计算它。通过这种方式,地址在编译时是已知的,而不是在滚动循环的情况下在运行时计算它们。由于检查边界而产生的分支成本也降低了。因此,每个循环嵌套都将具有最小展开阈值,这是其边界和在循环体中完成的性质计算的函数,超过该阈值展开将导致加速。展开可能不会在所有情况下都提供加速。 LLVM 等编译器允许您使用 -mllvm -unroll-count=U 指定展开因子,这样您就不必手动展开它。我确信 GCC 有一个等效的参数。您可以编写一个脚本来运行具有不同展开因子的循环,以测量加速并达到最佳展开计数。

滚动版:

for (x = 0; x < N; x++)
 {
     operation(x);
 }

Unroll count = 2:假设 N 为偶数,将迭代次数减少一半

for (x = 0; x < N; x+=2)
 {
     operation(x);
     operation(x+1);
 }

Unroll count = 4:将迭代次数减少到四分之一,假设 N 可以被 4 整除

for (x = 0; x < N; x+=4)
 {
     operation(x);
     operation(x+1);
     operation(x+2);
     operation(x+3);
 }

如果索引不能被展开计数整除,则需要一个残差循环来完成任务,这有​​其自身的开销。

Unroll count = 4:将迭代次数减少到四分之一,当 N 不能被 4 整除时

//main loop
for (x = 0; x <= N-4; x+=4)
 {
     operation(x);
     operation(x+1);
     operation(x+2);
     operation(x+3);
 }
 //residual loop
 for ( ; x < N; x++)
 {
     operation(y);
 }

另一种处理残差计算的方法是使用Duff's device,它基本上是基于开关的循环体实现,以确保循环的最后一次迭代处理残差计算,而无需完全编写单独的循环。

【讨论】:

  • 我根据你的建议添加了 Duff's。 @tom
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-03
相关资源
最近更新 更多