[英]How to optimize C++ avr code
To set or clear a bit in a register I use the following code: 要设置或清除寄存器中的位,我使用以下代码:
template<int... pos, class Int>
static constexpr void write_one(Int& i)
{
using expand = int[];
expand{0,((i |= (Int{1} << pos)), 0)...};
}
template<int... pos, class Int>
static constexpr void write_zero(Int& i)
{
using expand = int[];
expand{0,((i &= ~(Int{1} << pos)), 0)...};
}
It works fine. 它工作正常。 To test its efficiency I write 2 test functions:
为了测试它的效率,我编写了2个测试函数:
// The most efficiency
while(1){
PORTB |= (1 << PB0);
PORTB &= ~(1 << PB0);
}
// This is the one I want to measure
while(1){
Bit::write_one<PB0>(PORTB);
Bit::write_zero<PB0>(PORTB);
}
When I measure times with an oscilloscope the second one takes more time, so I disassamble the code getting the following: 当我使用示波器测量时间时,第二个需要更多时间,因此我将代码取消以下内容:
; This is the first one (of course, the most efficient)
000000c8 <_Z12testv>:
ce: 28 9a sbi 0x05, 0 ; 5
d0: 28 98 cbi 0x05, 0 ; 5
d2: fd cf rjmp .-6 ; 0xce <_Z12testv+0x6>
; The second one
000000c8 <_Z12testv>:
; The compiler optimize perfectly write_one<PB0>(PORTB)
ce: 28 9a sbi 0x05, 0 ; 5
; but, look what happens with write_zero<PB0>(PORTB)!!!
; Why the compiler can't write "cbi"???
; Here is the problem:
d0: 85 b1 in r24, 0x05 ; 5
d2: 90 e0 ldi r25, 0x00 ; 0
d4: 8e 7f andi r24, 0xFE ; 254
d6: 85 b9 out 0x05, r24 ; 5
d8: fa cf rjmp .-12 ; 0xce <_Z12testv+0x6>
I'm using avr-g++ 4.9.2 with -O3 flag. 我正在使用avr -g ++ 4.9.2和-O3标志。
template<int... pos, class Int>
static constexpr void write_one(Int& i)
{
using expand = int[];
expand{0,((i |= (Int{1} << pos)), 0)...};
}
template<int... pos, class Int>
static constexpr void write_zero(Int& i)
{
using expand = int[];
expand{0,((i &= ~(Int{1} << pos)), 0)...};
}
I
Bit::write_one<PB0>(PORTB);
Bit::write_zero<PB0>(PORTB);
int[2] { 0, ((i |= (Int{1} << pos)), 0) }
int[2] { 0, 0 } // a tmp that is a nop
(i |= (Int{1} << pos))
(i |= (decl_type(PORTB){1} << int { PB0 }))
PORTB looks like it is an volatile uint5_t possible values 0-31 ( AVR ) with the value 0x5 PORTB看起来像是一个具有值0x5的易失性uint5_t可能值0-31( AVR )
(i |= ( uint5_t{1} << int {0}))
1 and 0 are literals/constexpr so it gives 1. 1和0是literals / constexpr所以它给出1。
i |= 1
which code gens to 哪个代码适合
sbi 0x5, 0 // set port 5 bit 0
expand{0,((i &= ~(Int{1} << pos)), 0)...};
following the same logic gives 遵循相同的逻辑给出
(i &= ~(1))
gives the code 给出了代码
d0: 85 b1 in r24, 0x05 ; 5
d2: 90 e0 ldi r25, 0x00 ; 0 <---------- this value is not used nor set any flags???
d4: 8e 7f andi r24, 0xFE ; 254 (i &= ~(1))
d6: 85 b9 out 0x05, r24 ; 5
So the conclusion must be that there is an error in the code gen for AVR as it generates a spurious instruction. 因此,结论必须是AVR的代码生成错误,因为它会生成伪指令。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.