Пиша драйвер за ядрото на 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
Имайте предвид, че повече от малко могат да бъдат зададени наведнъж.
Така че това е кодът:
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. Преместване на 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 компилатора да развие този конкретен цикъл, но изглежда, че не е възможно според моето търсене...