簡體   English   中英

為什么嵌套for循環比展開相同代碼要慢得多?

[英]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 << SDAPORTB &= ~(1 << SDA)
  • PORTB三個操作

這就是循環的所有展開版本; 沒有別的需要做,甚至沒有轉移1向左j次,因為編譯器計算常量表達式,而1 << 4變得簡單16

另一方面,沒有展開的循環必須做額外的事情來保持循環:

  • 增加j
  • 比較j到8
  • 轉移1由左j位置
  • 無條件跳轉到循環的開頭

當循環展開時,CPU不再需要這些“非有效負載”指令,因此執行速度會提高。

為什么這種核心語言功能在這種情況下要慢得多?

許多現代編譯器會根據優化設置自動為您展開循環。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM