减少C ++代码中的循环时间有助于提高速度吗?

时间:2016-03-10 14:22:24

标签: c++ performance

我举了以下例子来说明我的问题:

void fun(int i, float *pt)
{
    // do something based on i
    std::cout<<*(pt+i)<<std::endl;
}
const unsigned int LOOP = 2000000007;

void fun_without_optmization()
{
    float *example;
    example = new float [LOOP];
    for(unsigned int i=0; i<LOOP; i++)
    {
        fun(i,example);            
    }
    delete []example;
}
void fun_with_optimization()
{
    float *example;
    example = new float [LOOP];
    unsigned int unit_loop = LOOP/10;
    unsigned int left_loop = LOOP%10;
    pt = example;
    for(unsigend int i=0; i<unit_loop; i++)
    {
        fun(0,pt); 
        fun(1,pt);  
        fun(2,pt);
        fun(3,pt);  
        fun(4,pt);
        fun(5,pt);
        fun(6,pt);  
        fun(7,pt);
        fun(8,pt);  
        fun(9,pt);
        pt=pt+10;
    }
    delete []example;
}

据我了解,函数fun_without_optimization()和函数fun_with_optimization()应该执行相同的操作。第二个函数优于第一个函数的唯一论据是fun中的指针计算变得简单。任何其他论点为什么第二个功能更好?

4 个答案:

答案 0 :(得分:6)

展开执行I / O的循环就像在伦敦JFK向东移动一英里的B747着陆带。

答案 1 :(得分:5)

回复:“为什么第二个功能更好?” - 你会接受答案解释为什么更好吗?

  1. 手动展开循环很容易出错,代码清楚地说明了这一点:您忘了处理尾部left_loop
  2. 至少在几十年内,编译器会为您进行此优化。
  3. 您如何知道在展开的循环中放入的最佳迭代次数?您是否针对特定的高速缓存大小并以字节为单位计算汇编指令的长度?编译器可能。
  4. 你弄乱其他干净的循环可能会阻止其他优化,比如使用SIMD。
  5. 底线是:如果您知道编译器没有的东西(运行时数据的特定模式,目标执行环境的详细信息等),并且您知道自己在做什么 - 您可以尝试手动循环展开。但即便如此 - 简介。

答案 2 :(得分:3)

您描述的技术称为loop unrolling;这可能会提高性能,因为评估控制结构的时间(更新循环变量和检查终止条件)变得更小。但是,正确的编译器可以为您完成此操作,并且如果手动完成,代码的可维护性会降低。

答案 3 :(得分:1)

这是一种用于并行体系结构的优化技术(支持VLIW指令的体系结构)。根据架构支持的DALU(最常见的4)和ALU(最常见的2)单元的数量,以及代码支持的“并行化”级别,可以在一个周期内执行多条指令。

所以这段代码:

for (int i=0; i<n;i++) //n multiple of 4, for simplicity
   a+=temp; //just a random instruction

如果重写如下,那么在并行体系结构上实际执行速度会更快:

for (int i=0;i<n ;i+=4)
{
    temp0 = temp0 +temp1; //reads and additions can be executed in parallel
    temp1 = temp2 +temp3;
    a=temp0+temp1+a;
}

您可以并行化代码的数量是有限的,这是CPU拥有的物理ALU / DALU所施加的限制。这就是为什么在尝试(正确)优化代码之前了解您的架构很重要。

它不会停在这里:你想要优化的代码必须是一个连续的代码块,这意味着没有跳转(没有函数调用,没有流程指令的机会),以实现最高效率。

编写代码,例如:

    for(unsigend int i=0; i<unit_loop; i++)
    {
        fun(0,pt); 
        fun(1,pt);  
        fun(2,pt);
        fun(3,pt);  
        fun(4,pt);
        fun(5,pt);
        fun(6,pt);  
        fun(7,pt);
        fun(8,pt);  
        fun(9,pt);
        pt=pt+10;
    } 

除非编译器内联函数调用,否则不会做太多事情;无论如何它看起来很多指示......

另一方面注意:虽然在优化代码时总是必须使用编译器,但是当你从代码中获得最大优化时,你绝不应该只依赖它。请记住,当您可能对特定情况感兴趣时,编译器会处理“一般情况” - 这就是为什么某些编译具有特殊指令来帮助优化过程的原因。