什么是流水线友好的代码?

本文探讨了流水线停滞的主要原因及优化方法,包括cache不命中、数据依赖和分支指令处理,通过改进循环展开、最小值优化和利用数组、查找表等技术减少分支判断,提升程序执行效率。

      流水线的工作原理和相关介绍参考【1】。

      通常情况下,流水线停滞主要由三方面原因导致:(1)cache不命中,(2)数据依赖,(3)分支指令。

      (1)cache不命中一方面因为芯片cache的容量有限,一方面也由程序局部性不强导致,本文不进行展开。     

 

      (2)数据依赖通常由编译器通过乱序来实现。将无数据依赖的指令提前补充到流水线中,相当于提前计算,有数据依赖的指令推迟执行。

      (3)分支指令,去掉分支指令有很多种方法,这种去掉分支指令的代码也叫做branch-free code

       分支结果数组化

       例如:下面的代码

  for(;;)
  {
      if(mode=0)
           sum+=12;
      else if(mode=1)
           sum+=2;
      else{}
   }

 

       可以改成如下数组

       static const  struct PATTERN  patterns[PATTERN_NUM] ={12,2};
       for(;;)
      {
            sum+=patterns[mode].cnt
      }

 

     类似的例子,The binary look-up table

      if (b) a = c;
      else a = d;
      这段代码可以改写为

      static const type lookup_table[] = { d, c };
      a = lookup_table[b];//b或者是0,或者是1。
      如果更复杂一点

      if (b1) a = c;
      else if (b2) a = d;
      else if (b3) a = e;
      else a = f;

      static const type lookup_table[] = { f, e, d, d, c, c, c, c };
      a = lookup_table[b1 * 4 + b2 * 2 + b3];

      另一种有趣的方案,其中x,x1..均为无符号整数。

      if ( x < x1 )
      {
           a = a0;
      }
      else if ( x < x2 )
      {
             a = a1;
      }
      else if ( x < x3 )
     {
             a = a2;
     }
     else if ( x < x4 )
     {
             a = a3;
     }
     else
     {
             a = a4;
     }

    uint32_t x_lt_x1  = (uint32_t)((int32_t)(x - x1) >> 31);
    uint32_t x_lt_x2  = (uint32_t)((int32_t)(x - x2) >> 31);
    uint32_t x_lt_x3  = (uint32_t)((int32_t)(x - x3) >> 31);
    uint32_t x_lt_x4  = (uint32_t)((int32_t)(x - x4) >> 31);
    uint32_t result_0 = (x_lt_x4 & a3 ) | (~x_lt_x4 & a4);
    uint32_t result_1 = (x_lt_x3 & a2 ) | (~x_lt_x3 & result_0);
    uint32_t result_2 = (x_lt_x2 & a1 ) | (~x_lt_x2 & result_1);
    uint32_t result   = (x_lt_x1 & a0 ) | (~x_lt_x1 & result_2);
    以上例子来源于:http://cellperformance.beyond3d.com/articles/2006/04/benefits-to-branch-elimination.html 
    循环展开,最小值优化

      int length = array.size;
      int min;
      for(int i = 0 ;i<length;i++)
      {
             if(array[i]>min)
                    min = array[i];
      }

      可以改写成

      int length = array.size;
      array.add_dummy_element(length%4);//增加dummy元素补齐到4的倍数
      int min;
      for(int i = 0 ;i<length;i+=4)
      {
             min = ((array[i]+min)-abs(array[i]-min))/2;           //min(a,b)=((a+b)-|a-b|)/2
             min = ((array[i+1]+min)-abs(array[i+1]-min))/2;
             min = ((array[i+2]+min)-abs(array[i+2]-min))/2;
             min = ((array[i+3]+min)-abs(array[i+3]-min))/2;
      }

 

     绝对值函数编译器会给与特别优化,内部不存在分支判断,这样一来不仅降低了i<length的数量,而且去掉了一次循环内的比较操作。

    告诉编译器那个分支的可能性更大

    详细参见: http://blog.youkuaiyun.com/pennyliang/archive/2009/06/20/4284396.aspx

    简言之,流水线通畅的代码一定是哪些顺序执行的代码,较少或者没有跳转的代码,数据依赖较少的代码。并且,数据都能在寄存器或者芯片缓存中获取的,否则都会导致流水线的迟滞,影响效率。

 

 

推荐阅读:

【1】http://www-cs-faculty.stanford.edu/~eroberts/courses/soco/projects/2000-01/risc/pipelining/index.html

【2】http://en.wikipedia.org/wiki/Compiler_optimization

【3】http://en.wikibooks.org/wiki/Optimizing_C%2B%2B/Code_optimization/Pipeline

 

### 标量流水线处理机的定义 标量流水线处理机是一种基于流水线技术的计算机处理器架构,其核心特点是每次只能处理一个数据元素。与向量处理器不同,标量处理器逐条执行指令并处理单一数据项[^2]。具体来说,标量流水线处理机通过将指令的执行过程划分为多个阶段(如取指令、译码、执行和存结果),每个阶段由专门的硬件单元完成,从而实现更高的吞吐量。 ### 标量流水线处理机的工作原理 标量流水线处理机的工作原理可以概括为以下几点: 1. **指令分解**:一条完整的指令被分解为多个子过程,例如取指令(Fetch)、译码(Decode)、执行(Execute)和存结果(Write Back)。每个子过程由特定的功能部件负责完成。 2. **流水线结构**:在流水线架构中,不同的指令可以在同一时间处于不同的执行阶段。例如,当第一条指令正在执行时,第二条指令可能正处于译码阶段,而第三条指令则可能正处于取指令阶段[^2]。 3. **时间效率**:假设每个子过程所需的时间为 Δt,则通过流水线技术,处理机可以在每个 Δt 期间完成一条新指令的某个阶段。对于 12 条指令,虽然首条指令需要 4Δt 完成所有阶段,但后续指令可以以 Δt 的间隔进入流水线,因此总耗时为 15Δt[^2]。 4. **性能提升**:通过流水线技术,标量流水线处理机能够显著提高指令的吞吐量,即使每个指令的实际执行时间并未减少。然而,需要注意的是,流水线中的条件转移等操作可能会导致流水线停顿或清空,从而影响性能[^1]。 ```python # 示例代码:模拟简单的标量流水线处理机 class ScalarPipeline: def __init__(self): self.stages = ["Fetch", "Decode", "Execute", "Write Back"] def process(self, instructions): for time_step in range(len(instructions) + len(self.stages) - 1): for i in range(len(instructions)): if time_step - i >= 0 and time_step - i < len(self.stages): print(f"Time {time_step}: Instruction {i} at stage {self.stages[time_step - i]}") ```
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值