【发布时间】:2012-09-13 07:23:21
【问题描述】:
我正在编写一个 Linux 内核驱动程序(for 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
请注意,一次可以设置多个位。
所以这是代码:
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);
}
}
(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);
}
}
2. Shift 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);
}
}
3. 完全展开循环(未显示)。那会让代码有点乱。
4.还有其他更好的方法吗?
5. 还是说编译器实际上会生成最优化的方式?
编辑:我正在寻找一种方法来告诉 gcc 编译器展开该特定循环,但根据我的搜索似乎不可能......
【问题讨论】:
-
您只有 17 个元素需要处理。手动展开它并不比第一个示例中的代码更混乱
标签: c linux-kernel bit-manipulation loop-unrolling