[英]Why is this nested for loop so much slower than unrolling the same code?
我正在使用ATtiny85和128x64px OLED構建一個小玩具控制台。 在我的初始構建中,我使用內置的shiftOut()
和digitalWrite()
函數將顯示數據移出到屏幕控制器。
這讓我大約5fps,這有點令人失望。
我編寫了自己的函數,使用直接端口操作來發送數據並且速度大幅提高~23fps,這也不錯。 這是功能:
void shift_out_block(block)
{
byte b;
for (byte i = 0; i < 8; i++)
{
b = pgm_read_byte(block+i);
for (byte j=0 ; j < 8 ; j++)
{
if ( !!( b & (1 << j)) )
{
PORTB |= 1 << SDA;
}
else
{
PORTB &= ~(1 << SDA);
}
PORTB |= 1 << SCL; // HIGH
PORTB &= ~(1 << SCL); // LOW
}
}
}
23fps還可以,但它不是30甚至60fps(如果它是24fps,我實際上已經把它留在了這里,但奇數......)。
我理解為什么刪除庫調用和操作端口直接改進了很多東西 - 庫被編寫為適用於各種不同的MCU。
我依稀記得循環解體是一個事情 ,所以我解開內for
循環:
void shift_out_block()
{
byte b;
for (byte i = 0; i < 8; i++)
{
b = pgm_read_byte(block+i);
if ( !!( b & (1 << 0)) )
{
PORTB |= 1 << SDA;
}
else
{
PORTB &= ~(1 << SDA);
}
PORTB |= 1 << SCL; // HIGH
PORTB &= ~(1 << SCL); // LOW
if ( !!( b & (1 << 1)) )
{
PORTB |= 1 << SDA;
}
else
{
PORTB &= ~(1 << SDA);
}
PORTB |= 1 << SCL; // HIGH
PORTB &= ~(1 << SCL); // LOW
if ( !!( b & (1 << 2)) )
{
PORTB |= 1 << SDA;
}
else
{
PORTB &= ~(1 << SDA);
}
PORTB |= 1 << SCL; // HIGH
PORTB &= ~(1 << SCL); // LOW
if ( !!( b & (1 << 3)) )
{
PORTB |= 1 << SDA;
}
else
{
PORTB &= ~(1 << SDA);
}
PORTB |= 1 << SCL; // HIGH
PORTB &= ~(1 << SCL); // LOW
if ( !!( b & (1 << 4)) )
{
PORTB |= 1 << SDA;
}
else
{
PORTB &= ~(1 << SDA);
}
PORTB |= 1 << SCL; // HIGH
PORTB &= ~(1 << SCL); // LOW
if ( !!( b & (1 << 5)) )
{
PORTB |= 1 << SDA;
}
else
{
PORTB &= ~(1 << SDA);
}
PORTB |= 1 << SCL; // HIGH
PORTB &= ~(1 << SCL); // LOW
if ( !!( b & (1 << 6)) )
{
PORTB |= 1 << SDA;
}
else
{
PORTB &= ~(1 << SDA);
}
PORTB |= 1 << SCL; // HIGH
PORTB &= ~(1 << SCL); // LOW
if ( !!( b & (1 << 7)) )
{
PORTB |= 1 << SDA;
}
else
{
PORTB &= ~(1 << SDA);
}
PORTB |= 1 << SCL; // HIGH
PORTB &= ~(1 << SCL); // LOW
}
}
毫不費力,復制粘貼7次。 給我近75fps - 原始功能在~42ms執行,新的丑陋只需要~13ms。
出於興趣,我將發送位部分作為一個單獨的函數打破並調用了8次:
void shift_out_bit(bool bit)
{
if ( bit )
{
PORTB |= 1 << SDA;
}
else
{
PORTB &= ~(1 << SDA);
}
PORTB |= 1 << SCL; // HIGH
PORTB &= ~(1 << SCL); // LOW
}
void shift_out_block()
{
byte b;
for (byte i = 0; i < 8; i++)
{
b = pgm_read_byte(block+i);
shift_out_bit( !!( b & (1 << 0)) );
shift_out_bit( !!( b & (1 << 1)) );
shift_out_bit( !!( b & (1 << 2)) );
shift_out_bit( !!( b & (1 << 3)) );
shift_out_bit( !!( b & (1 << 4)) );
shift_out_bit( !!( b & (1 << 5)) );
shift_out_bit( !!( b & (1 << 6)) );
shift_out_bit( !!( b & (1 << 7)) );
}
}
〜22ms執行,或45.4545454545 fps,這甚至不是一個很好的數字。
我不是由任何發揮想象力的C程序員- Python是我平常出沒的地方(我最初開始在Python / RPI這個項目,但很快就放棄了這一點!)。
為什么這種核心語言功能在這種情況下要慢得多? 隨着我的項目變得更加復雜,我應該考慮哪些其他優化措施?
考慮在最內層循環內完成的“有效負載”操作:
b
中的特定位 PORTB |= 1 << SDA
與PORTB &= ~(1 << SDA)
PORTB
三個操作 這就是循環的所有展開版本; 沒有別的需要做,甚至沒有轉移1
向左j
次,因為編譯器計算常量表達式,而1 << 4
變得簡單16
。
另一方面,沒有展開的循環必須做額外的事情來保持循環:
j
j
到8 1
由左j
位置 當循環展開時,CPU不再需要這些“非有效負載”指令,因此執行速度會提高。
為什么這種核心語言功能在這種情況下要慢得多?
許多現代編譯器會根據優化設置自動為您展開循環。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.