c - 循环展开,性能实验室

标签 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].{红,绿,蓝} = {22543,1426,53562}

dst[9120].{红,绿,蓝} = {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;
        }
    }
}

最佳答案

循环展开的基本思想是在循环体中多次显式地编写计算,而不是让编译器根据循环边界和条件来计算。这样,在滚动循环的情况下,地址在编译时就已知,而不是在运行时计算它们。由于检查边界而导致的分支成本也降低了。因此,每个循环嵌套将具有最小展开阈值,该阈值是其边界和循环体中完成的自然计算的函数,超过该阈值展开将导致加速。展开可能无法在所有情况下提供加速。 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这基本上是基于开关的循环体实现,以确保循环的最后一次迭代处理残差计算,而不必完全编写单独的循环。

关于c - 循环展开,性能实验室,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47215369/

相关文章:

c - 使用相同代码的 clang 和 gcc 之间的不同结果

c - Arduino 无线选项

optimization - 警告 : 'chart.js' . CommonJS 或 AMD 依赖项可能导致优化救助

mysql - 如果我的应用程序在保存数据之前已经验证了数据,为什么我需要对列使用 UNIQUE 约束?

haskell - 是否有类似于函数式编程的循环展开的优化?

c - 如何在 struct 内部和 main 外部分配内存?

c - 使用GDB在C中查找局部变量的地址

c - 优化以下代码块

static - 静态数组的展开循环

c - 展开嵌套的 for 循环 - C