循环展开,效果实验室

时间:2017-11-10 03:47:38

标签: c optimization loop-unrolling

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

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;   
        }
    }
}

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

“错误:尺寸= 96,9216错误

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

src [9215]。{red,green,blue} = {22543,1426,53562}

dst [9120]。{red,green,blue} = {0,0,0}“

对于我做错了什么或我应该做什么的任何帮助表示赞赏

EDIT 我用这个

更新了我的代码
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;
        }
    }
}

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

EDIT 我将代码更新为

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;
        }
    }
}

1 个答案:

答案 0 :(得分:1)

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

滚动版本:

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

展开次数= 2 :假设N均为

,将迭代次数减少一半
for (x = 0; x < N; x+=2)
 {
     operation(x);
     operation(x+1);
 }

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

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

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

展开次数= 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,它基本上是基于循环体的循环实现,以确保循环的最后一次迭代处理剩余计算,而不必完全写一个单独的循环。 / p>