簡體   English   中英

優化ARM Cortex M3代碼

[英]Optimizing ARM Cortex M3 code

我有一個C函數,它試圖將幀緩沖區復制到FSMC RAM。

這些函數將游戲循環的幀速率降低到10FPS。 我想知道如何分析反匯編函數,我應該計算每個指令周期嗎? 我想知道CPU在哪里花費時間,在哪一部分。 我確定算法也是一個問題,因為它的O(N ^ 2)

C函數是:

void LCD_Flip()
{

    u8  i,j;


    LCD_SetCursor(0x00, 0x0000);
    LCD_WriteRegister(0x0050,0x00);//GRAM horizontal start position
    LCD_WriteRegister(0x0051,239);//GRAM horizontal end position
    LCD_WriteRegister(0x0052,0);//Vertical GRAM Start position
    LCD_WriteRegister(0x0053,319);//Vertical GRAM end position
    LCD_WriteIndex(0x0022);

    for(j=0;j<fbHeight;j++)
    {
        for(i=0;i<240;i++)
        {
            u16 color = frameBuffer[i+j*fbWidth];
            LCD_WriteData(color);

        }
    }

}

拆卸功能:

08000fd0 <LCD_Flip>:
 8000fd0:   b580        push    {r7, lr}
 8000fd2:   b082        sub sp, #8
 8000fd4:   af00        add r7, sp, #0
 8000fd6:   2000        movs    r0, #0
 8000fd8:   2100        movs    r1, #0
 8000fda:   f7ff fde9   bl  8000bb0 <LCD_SetCursor>
 8000fde:   2050        movs    r0, #80 ; 0x50
 8000fe0:   2100        movs    r1, #0
 8000fe2:   f7ff feb5   bl  8000d50 <LCD_WriteRegister>
 8000fe6:   2051        movs    r0, #81 ; 0x51
 8000fe8:   21ef        movs    r1, #239    ; 0xef
 8000fea:   f7ff feb1   bl  8000d50 <LCD_WriteRegister>
 8000fee:   2052        movs    r0, #82 ; 0x52
 8000ff0:   2100        movs    r1, #0
 8000ff2:   f7ff fead   bl  8000d50 <LCD_WriteRegister>
 8000ff6:   2053        movs    r0, #83 ; 0x53
 8000ff8:   f240 113f   movw    r1, #319    ; 0x13f
 8000ffc:   f7ff fea8   bl  8000d50 <LCD_WriteRegister>
 8001000:   2022        movs    r0, #34 ; 0x22
 8001002:   f7ff fe87   bl  8000d14 <LCD_WriteIndex>
 8001006:   2300        movs    r3, #0
 8001008:   71bb        strb    r3, [r7, #6]
 800100a:   e01b        b.n 8001044 <LCD_Flip+0x74>
 800100c:   2300        movs    r3, #0
 800100e:   71fb        strb    r3, [r7, #7]
 8001010:   e012        b.n 8001038 <LCD_Flip+0x68>
 8001012:   79f9        ldrb    r1, [r7, #7]
 8001014:   79ba        ldrb    r2, [r7, #6]
 8001016:   4613        mov r3, r2
 8001018:   011b        lsls    r3, r3, #4
 800101a:   1a9b        subs    r3, r3, r2
 800101c:   011b        lsls    r3, r3, #4
 800101e:   1a9b        subs    r3, r3, r2
 8001020:   18ca        adds    r2, r1, r3
 8001022:   4b0b        ldr r3, [pc, #44]   ; (8001050 <LCD_Flip+0x80>)
 8001024:   f833 3012   ldrh.w  r3, [r3, r2, lsl #1]
 8001028:   80bb        strh    r3, [r7, #4]
 800102a:   88bb        ldrh    r3, [r7, #4]
 800102c:   4618        mov r0, r3
 800102e:   f7ff fe7f   bl  8000d30 <LCD_WriteData>
 8001032:   79fb        ldrb    r3, [r7, #7]
 8001034:   3301        adds    r3, #1
 8001036:   71fb        strb    r3, [r7, #7]
 8001038:   79fb        ldrb    r3, [r7, #7]
 800103a:   2bef        cmp r3, #239    ; 0xef
 800103c:   d9e9        bls.n   8001012 <LCD_Flip+0x42>
 800103e:   79bb        ldrb    r3, [r7, #6]
 8001040:   3301        adds    r3, #1
 8001042:   71bb        strb    r3, [r7, #6]
 8001044:   79bb        ldrb    r3, [r7, #6]
 8001046:   2b63        cmp r3, #99 ; 0x63
 8001048:   d9e0        bls.n   800100c <LCD_Flip+0x3c>
 800104a:   3708        adds    r7, #8
 800104c:   46bd        mov sp, r7
 800104e:   bd80        pop {r7, pc}

不完全回答你的問題,但我看到你渴望快速執行循環。

以下是本書的一些提示:“ARM系統開發人員指南:設計和優化系統軟件(計算機體系結構和設計中的Morgan Kaufmann系列)” http://www.amazon.com/ARM-System-Developers-Guide-Architecture / DP / 1558608745

第5章包含名為“C循環結構”的部分。 以下是該部分的摘要:

有效地編寫循環

  • 使用倒數為零的循環。 然后編譯器不需要分配寄存器來保存終止值,並且與零的比較是免費的。
  • 默認情況下使用無符號循環計數器,並且連續條件為i!= 0而不是i> 0。 這將確保循環開銷僅為兩條指令。
  • 當您知道循環將至少迭代一次時,請使用do-while循環而不是for循環。 這樣可以保存編譯器檢查以查看循環計數是否為零。
  • 展開重要循環以減少循環開銷。 不要過度使用。 如果循環開銷占總數的一小部分,那么展開會增加代碼大小並損害緩存的性能。
  • 嘗試安排數組中元素的數量是四或八的倍數。 然后,您可以輕松地將循環展開兩次,四次或八次,而無需擔心剩余的數組元素。

根據摘要,您的內部循環可能如下所示。

uinsigned int i = 240/4;  // Use unsigned loop counters by default
                          // and the continuation condition i!=0

do
{
    // Unroll important loops to reduce the loop overhead
    LCD_WriteData( (u16)frameBuffer[ (i--) + (j*fbWidth) ] );
    LCD_WriteData( (u16)frameBuffer[ (i--) + (j*fbWidth) ] );
    LCD_WriteData( (u16)frameBuffer[ (i--) + (j*fbWidth) ] );
    LCD_WriteData( (u16)frameBuffer[ (i--) + (j*fbWidth) ] );
}
while ( i != 0 )  // Use do-while loops rather than for
                  // loops when you know the loop will
                  // iterate at least once

您可能還想嘗試使用'pragma',例如:

#pragma Otime

http://www.keil.com/support/man/docs/armcc/armcc_chr1359124989673.htm

#pragma unroll(n)

http://www.keil.com/support/man/docs/armcc/armcc_chr1359124992247.htm

因為它是Cortex-M3試圖找出MCU硬件是否有機會安排代碼/數據以利用其哈佛架構 (我的速度提高了30%)。

看到我的另一個答案

也許並非所有內容都適用於您的應用程序(以相反的順序填充緩沖區)。 我只是想提請你注意這本書以及可能的優化要點。

您應該首先在啟用速度優化的情況下編譯C代碼。 您提供的反匯編代碼似乎是在堆棧中存儲ij計數器,這會向內部循環添加3個加載/存儲操作。 您可能LCD_WriteData在內部循環中內聯LCD_WriteData

另一方面,如果您真的在內循環中寫入LCD,那么性能可能會受到該接口的限制。

只是為了純粹減少循環操作的數量,你可以這樣做。 我確實做了一些可能不准確的假設:你有一個從i=0:239開始的循環,我假設fbWidth240相同。 如果不是這樣,那么循環必須更復雜。

void LCD_Flip()
{
    u16 i,limit = fbHeight+fbWidth;
    // We will use a precalculated limit and one single loop

    LCD_SetCursor(0x00, 0x0000);
    LCD_WriteRegister(0x0050,0x00);//GRAM horizontal start position
    LCD_WriteRegister(0x0051,239);//GRAM horizontal end position
    LCD_WriteRegister(0x0052,0);//Vertical GRAM Start position
    LCD_WriteRegister(0x0053,319);//Vertical GRAM end position
    LCD_WriteIndex(0x0022);

    // Single loop from 0:limit-1 takes care of having to do an
    // x,y conversion each iteration.
    for(i=0;i<limit;j++)
    {
        u16 color = frameBuffer[i];
        LCD_WriteData(color);
    }
}

這剝離了兩個循環,有利於單個for循環,每次迭代只有一個條件測試。 最重要的是,對frameBuffer的索引現在是線性的,因此我們不需要將寬度乘以從x,y到線性存儲。 你的循環迭代不會減少(即它仍然是O(N)N = height*width ),但指令的數量應該減少。

正如@Joe Hass在他的回答中指出的那樣,如果你真的受到LCD界面的限制,這實際上可能根本沒有幫助。 根據您使用的STM32,FSMC可能不會特別快,我無法想象LCD控制器也會非常快。

暫無
暫無

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

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