循环展开(使用按位运算)

Alv*_*ong 6 c bit-manipulation linux-kernel loop-unrolling

我正在编写Linux内核驱动程序(用于ARM),在irq处理程序中我需要检查中断位.

bit
 0/16  End point 0 In/Out interrupt
       (very likely, while In is more likely)
 1/17  End point 1 In/Out interrupt
 ...
15/31  End point 15 In/Out interrupt
Run Code Online (Sandbox Code Playgroud)

请注意,一次可以设置多个位.

所以这是代码:

int i;
u32 intr = read_interrupt_register();

/* ep0 IN */
if(likely(intr & (1 << 0))){
    handle_ep0_in();
}

/* ep0 OUT */
if(likely(intr & (1 << 16))){
    handle_ep0_out();
}

for(i=1;i<16;++i){
    if(unlikely(intr & (1 << i))){
        handle_ep_in(i);
    }
    if(unlikely(intr & (1 << (i + 16)))){
        handle_ep_out(i);
    }
}
Run Code Online (Sandbox Code Playgroud)

(1 << 0)(1 << 16)会在编译时被然而计算,(1 << i)(1 << (i + 16))不会.循环中还将进行积分比较和加法.

因为它是一个irq处理程序,所以应该在最短的时间内完成工作.这让我想一想我是否需要优化它.

可能的方法?

1.拆分循环,似乎有什么区别?

/* ep0 IN */
if(likely(intr & (1 << 0))){
    handle_ep0_in();
}

/* ep0 OUT */
if(likely(intr & (1 << 16))){
    handle_ep0_out();
}

for(i=1;i<16;++i){
    if(unlikely(intr & (1 << i))){
        handle_ep_in(i);
    }
}
for(i=17;i<32;++i){
    if(unlikely(intr & (1 << i))){
        handle_ep_out(i - 16);
    }
}
Run Code Online (Sandbox Code Playgroud)

2.移位intr代替的值进行比较,以?

/* ep0 IN */
if(likely(intr & (1 << 0))){
    handle_ep0_in();
}

/* ep0 OUT */
if(likely(intr & (1 << 16))){
    handle_ep0_out();
}

for(i=1;i<16;++i){
    intr >>= 1;
    if(unlikely(intr & 1)){
        handle_ep_in(i);
    }
}
intr >>= 1;
for(i=1;i<16;++i){
    intr >>= 1;
    if(unlikely(intr & 1)){
        handle_ep_out(i);
    }
}
Run Code Online (Sandbox Code Playgroud)

3.完全展开循环(未显示).这会使代码有点混乱.

4.还有其他更好的方法吗?

5.或者编译器实际上会生成最优化的方式?


编辑:我正在寻找一种方法来告诉gcc编译器展开那个特定的循环,但似乎根据我的搜索它是不可能的...

Nil*_*nck 5

如果我们可以假设intr中的设置位数很少(通常在中断掩码中就是这种情况),我们可以优化一点并编写一个仅为每个位执行一次的循环:

void handle (int intr)
{
  while (intr)
  {
    // find index of lowest bit set in intr:
    int bit_id = __builtin_ffs(intr)-1;

    // call handler:
    if (bit_id > 16)
      handle_ep_out (bit_id-16);
    else
      handle_ep_in (bit_id);

    // clear that bit
    // (I think there was a bit-hack out there to simplify this step even further)
    intr -= (1<<bit_id);
  }
}
Run Code Online (Sandbox Code Playgroud)

在大多数ARM体系结构中,__ builtin_ffs将编译为CLZ指令并围绕它进行一些算术运算.除了ARM7和更旧的内核之外,它应该这样做.

另外:当在嵌入式设备上编写中断处理函数的尺寸使得性能的差异,以及由于指令都被加载到代码缓存.精益代码通常执行得更快.如果将内存访问保存到不太可能在缓存中的内存,则可以节省一些开销.