【问题标题】:Removing if statement from loop [duplicate]从循环中删除 if 语句 [重复]
【发布时间】:2018-04-20 12:41:58
【问题描述】:

我在 C 中有以下循环,我试图从中删除 if 语句,以便每次迭代都不会检查它们。然而,我正在努力做到这一点。有没有人对我如何将它们取出有任何建议?

for(ix=0;ix<Nx;ix++) {
  for(iy=0;iy<Ny;iy++) {
    if (ix==0) {
      pudx = (u[1][iy] + u[Nx-1][iy] - 2.0*u[0][iy])*(calc1);   
    } else if (ix==Nx-1) {
      pudx = (u[0][iy] + u[Nx-2][iy] - 2.0*u[Nx-1][iy])*(calc1);  
    } else {
      pudx = (u[ix+1][iy] + u[ix-1][iy] - 2.0*u[ix][iy])*(calc1);
    }
    if (iy==0) {
      pudy = (u[ix][1] + u[ix][Ny-1] - 2.0*u[ix][0])*(calc2);    
    } else if (iy==Ny-1) {
      pudy = (u[ix][0] + u[ix][Ny-2] - 2.0*u[ix][Ny-1])*(calc2);   
    } else {
      pudy = (u[ix][iy+1] + u[ix][iy-1] - 2.0*u[ix][iy])*(calc2);
    }
    u_new[ix][iy] = 2.0*u[ix][iy] - u_old[ix][iy] + calc*(pudx+pudy);
  }
}

【问题讨论】:

  • 看看使用“switch - case”
  • 看起来你是循环的第一次和最后一次迭代的特殊外壳......你应该能够更改循环测试和初始化,并且只需在循环内使用语句的 else 部分。
  • 如果你能正确缩进你的代码会很有帮助。
  • 一个像样的编译器很有可能,例如gcc 的最新版本,将为您解决这个问题。
  • @Gene,我没有看到 gcc 7.2.0 上的 -O2 有很多优化。与 OPs 版本相比,特殊外壳版本在我的机器上减少了 31% 的运行时间。使用 -O3,我看到运行时间减少了 73%。这让我很吃惊。

标签: c loops optimization


【解决方案1】:
for(ix=0;ix<Nx;ix++) {
    for(iy=0;iy<Ny;iy++) {

        pudx = (u[(ix+1)%Nx][iy] + u[(Nx+ix-1)%Nx][iy] - 2.0*u[ix][iy])*(calc1); 
        pudy = (u[ix][(iy+1)%Ny] + u[ix][(Ny+iy-1)%Ny] - 2.0*u[ix][iy])*(calc2);

        u_new[ix][iy] = 2.0*u[ix][iy] - u_old[ix][iy] + calc*(pudx+pudy);
    }
}

这不是优化。它只是按照您的要求删除了 if 条件。

【讨论】:

  • 这可能会减少代码大小,但由于添加了所有剩余操作(本质上是除法),它可能会比原来运行得慢。
  • 是的..我同意。
  • @TomKarzes 但是条件跳转不是当今计算机最慢的操作之一吗?
  • @Cppplus1 这取决于处理器,但我怀疑大多数机器上的除法速度较慢,尤其是多除法。
  • 如果 NxNy 都是 2 的硬编码幂,那么这段代码很可能比条件分支代码快得多。如果不是,则取决于架构。
【解决方案2】:

我重写了“序幕、主循环、尾声”语义。

void update_u_new(double **u_new, double **u, double **u_old, int ix, int iy, double calc, double pudx, double pudy)
{
    u_new[ix][iy] = 2.0*u[ix][iy] - u_old[ix][iy] + calc*(pudx+pudy);   
}

//iterates iy=1 to Ny-1
void main_loop_iy(double **u, double **u_new, double **u_old, int ix, int Ny, double pudx, double calc2, double calc)
{
    int iy;
    for(iy=1; iy<Ny-1; ++iy)
    {
        double pudy = (u[ix][iy+1] + u[ix][iy-1] - 2.0*u[ix][iy])*(calc2);  
        update_u_new(u_new, u, u_old, ix, iy, calc, pudx, pudy); 
    }
}

void do_iy_given_ux(double **u, double **u_new, double **u_old, double *ux_m1, double *ux, double *ux_p1, int ix, int Ny, double calc1, double calc2, double calc)
{
    //prologue iy=0
    int iy=0;
    double pudx = (ux_p1[iy] + ux_m1[iy] - 2.0*ux[iy])*(calc1); 
    double pudy = (u[ix][1] + u[ix][Ny-1] - 2.0*u[ix][0])*(calc2);
    update_u_new(u_new, u, u_old, ix, iy, calc, pudx, pudy); 

    //main loop 1 <= iy < Ny-1
    main_loop_iy(u, u_new, u_old, ix, Ny, pudx, calc2, calc);

    //epilogue, iy = Ny-1
    iy = Ny-1;   
    pudy = (u[ix][0] + u[ix][Ny-2] - 2.0*u[ix][Ny-1])*(calc2); 
    update_u_new(u_new, u, u_old, ix, iy, calc, pudx, pudy); 
}

//assumes ix=0
void do_iy_ix0(double **u, double **u_new, double **u_old, int Nx, int Ny, double calc1, double calc2, double calc)
{
    int ix=0;
    do_iy_given_ux(u, u_new, u_old, u[Nx-1], u[ix], u[ix+1], ix, Ny, calc1, calc2, calc);
}

//assumes ix=1 to Nx-1
void do_iy_given_ix_main_loop(double **u, double **u_new, double **u_old, int ix, int Ny, double calc1, double calc2, double calc)
{
    do_iy_given_ux(u, u_new, u_old, u[ix-1], u[ix], u[ix+1], ix, Ny, calc1, calc2, calc);
}

//assumes ix=Nx-1
void do_iy_ixNm1(double **u, double **u_new, double **u_old, int Nx, int Ny, double calc1, double calc2, double calc)
{
    int ix = Nx-1;
    do_iy_given_ux(u, u_new, u_old, u[0], u[ix], u[ix-1], ix, Ny, calc1, calc2, calc);
}

int do_it(double **u, double **u_new, double **u_old, int Nx, int Ny, double calc, double calc1, double calc2)
{
    int ix = 0;

    //prologue ix=0
    do_iy_ix0(u, u_new, u_old, Nx, Ny, calc1, calc2, calc);

    //main loop on 1 <= ix < Nx-1
    for(ix=1; ix<Nx-1; ++ix)
    {
        do_iy_given_ix_main_loop(u, u_new, u_old, ix, Ny, calc1, calc2, calc);
    }

    //epilogue on ix = Nx-1
    do_iy_ixNm1(u, u_new, u_old, Nx, Ny, calc1, calc2, calc);

    return 0;
}

【讨论】:

    【解决方案3】:

    这是一个简单的解决方案:将if 的内容从循环中取出并使其更加明确。它更长,但它使您免于可能耗时的分支和模/除法计算。我使用预处理器宏来让事情变得更简洁,并减少复制粘贴错误的机会。宏应本地化为相关代码,否则calc1u_old 等变量将导致编译错误。基本上,您可以用以下代码替换有问题的循环:

    #define PUDX0 ((u[1][iy] + u[Nx-1][iy] - 2.0*u[0][iy])*(calc1))
    #define PUDX ((u[ix+1][iy] + u[ix-1][iy] - 2.0*u[ix][iy])*(calc1))
    #define PUDXN ((u[0][iy] + u[Nx-2][iy] - 2.0*u[Nx-1][iy])*(calc1))
    
    #define PUDY0 ((u[ix][1] + u[ix][Ny-1] - 2.0*u[ix][0])*(calc2))
    #define PUDY ((u[ix][iy+1] + u[ix][iy-1] - 2.0*u[ix][iy])*(calc2))
    #define PUDYN ((u[ix][0] + u[ix][Ny-2] - 2.0*u[ix][Ny-1])*(calc2))
    
    #define UNEW(x, y, pudx, pudy) u_new[x][y] = 2.0*u[x][y] - u_old[x][y] + calc*(pudx+pudy)
    
    // ix = 0, iy = [0..Ny-1]
    UNEW(0, 0, PUDX0, PUDY0);
    for (iy=1; iy < Ny-1; iy++) {
        UNEW(0, iy, PUDX0, PUDY);
    }
    UNEW(0, iy, PUDX0, PUDYN);
    
    // ix = [1..Nx-1], iy = Ny-1
    for (ix=1; ix < Nx-1; ix++) {
        UNEW(ix, iy, PUDX, PUDYN);
    }
    UNEW(ix, iy, PUDXN, PUDYN);
    
    // ix = Nx-1, iy = [0..Ny-2]
    UNEW(ix, 0, PUDXN, PUDY0);
    for (iy=1; iy < Ny-1; iy++) {
        UNEW(ix, iy, PUDXN, PUDY);
    }
    
    // ix = [1..Nx-2], iy = 0
    for (ix=1; ix < Nx-1; ix++) {
        UNEW(ix, 0, PUDX, PUDY0);
    }
    
    // ix = [1..Nx-2], iy = [1..Ny-2]
    for (ix=1; ix < Nx-1; ix++) {
        for (iy=1; iy < Ny-1; iy++) {
            UNEW(ix, iy, PUDX, PUDY);
        }
    }
    
    #undef UNEW
    #undef PUDYN
    #undef PUDY
    #undef PUDY0
    #undef PUDXN
    #undef PUDX
    #undef PUDX0
    

    然而,这显然是供计算机处理的,而不是供人类阅读和稍后修改的。宏和 cmets 也是一种尝试,以帮助人们在以后需要更改时提供帮助。除非原始代码中存在严重的性能问题,否则我个人更喜欢带有分支语句的原始循环。它可能会慢一些,但如果需要调试起来会容易得多!

    【讨论】:

    • 那些 PUD 宏体需要用括号括起来。事实上,在使用它们的上下文中,它们隐含地依赖于乘法比加法绑定更紧密的事实。
    • 您缺少几个分号。否则,我喜欢宏。 :)
    • @MFisherKDX 除了他们无人看管。永远不要将表达式宏写成(a)*(b)总是 用括号括起来,如((a)*(b))。这样就不会在扩展后出现意外关联的风险。
    • 我遇到分段错误,不知道为什么
    • @WillC 抱歉,您遇到了麻烦。我唯一做的就是将特定案例的计算从循环中解脱出来。这是计算行的简单复制和粘贴操作。宏只是让我使它成为半可读的,我只是仔细检查了正确的参数是否传递给UNEW 宏。它的段错误在哪里?
    猜你喜欢
    • 1970-01-01
    • 2018-10-19
    • 1970-01-01
    • 2021-11-26
    • 2012-04-19
    • 1970-01-01
    • 1970-01-01
    • 2018-05-21
    • 1970-01-01
    相关资源
    最近更新 更多