2017-08-29 35 views
2

我想知道這是否是更優化的使用本地函數(以下_drawBitmap的爲例)誰只需要3個參數不能被內聯因爲函數訪問一些業主的過程變量,或使用全局函數可以內聯(但它會真正內聯嗎?),這將需要5參數使用本地函數或全局函數更優化嗎?

也不知道它是重要的,但是這個代碼主要是針對Android/IOS編譯

代碼採用局部功能:

procedure TMyObject.onPaint(Sender: TObject; Canvas: TCanvas; const ARect: TRectF); 

    function _drawBitmap(const aBitmap: {$IFDEF _USE_TEXTURE}TTexture{$ELSE}Tbitmap{$ENDIF}; const aTopLeft: TpointF; Const aOpacity: Single): boolean; 
    var aDestRect: TrectF; 
    begin 
    Result := False; 
    if aBitmap <> nil then begin 

     //calculate aDestRect 
     aDestRect := canvas.AlignToPixel(
        TRectF.Create(
         aTopLeft, 
         aBitmap.Width/ScreenScale, 
         aBitmap.Height/ScreenScale)); 

     //if the aBitmap is visible 
     if ARect.IntersectsWith(aDestRect) then begin 

     Result := True; 

     {$IFDEF _USE_TEXTURE} 
     TCustomCanvasGpu(Canvas).DrawTexture(aDestRect, // ATexRect 
              TRectF.Create(0, 
                  0, 
                  aBitmap.Width, 
                  aBitmap.Height), // ARect 
              ALPrepareColor(TCustomCanvasGpu.ModulateColor, aOpacity * AbsoluteOpacity), // https://quality.embarcadero.com/browse/RSP-15432 
              aBitmap); 
     {$ELSE} 
     Canvas.DrawBitmap(aBitmap, // ABitmap 
          TRectF.Create(0, 
             0, 
             aBitmap.Width, 
             aBitmap.Height), // SrcRect 
          aDestRect, // DstRect 
          aOpacity * AbsoluteOpacity, // AOpacity 
          samevalue(aDestRect.Width, aBitmap.Width, Tepsilon.Position) and 
          samevalue(aDestRect.height, aBitmap.height, Tepsilon.Position)); // HighSpeed - set interpolation to none 
     {$ENDIF}; 

     end; 

    end; 
    end; 

begin 

    _drawBitmap(aBitmap, aPos, 1); 

end; 

ASM:

MyObject.pas.2632: _drawBitmap(fBtnFilterBitmap, // aBitmap 
00B97511 55    push ebp 
00B97512 680000803F  push $3f800000 
00B97517 8B45F8   mov eax,[ebp-$08] 
00B9751A 8D90C4050000  lea edx,[eax+$000005c4] 
00B97520 8B45F8   mov eax,[ebp-$08] 
00B97523 8B80A8040000  mov eax,[eax+$000004a8] 
00B97529 E882FDFFFF  call _drawBitmap 
00B9752E 59    pop ecx 


MyObject.pas.2562: begin 
00B972B0 55    push ebp 
00B972B1 8BEC    mov ebp,esp 
00B972B3 83C4A0   add esp,-$60 
00B972B6 53    push ebx 
00B972B7 56    push esi 
00B972B8 57    push edi 
00B972B9 8955FC   mov [ebp-$04],edx 
00B972BC 8BF0    mov esi,eax 


MyObject.pas.2563: Result := False; 
00B972BE 33DB    xor ebx,ebx 
MyObject.pas.2564: if aBitmap <> nil then begin 
00B972C0 85F6    test esi,esi 
00B972C2 0F84B4010000  jz $00b9747c 
MyObject.pas.2567: aDestRect := canvas.AlignToPixel(
00B972C8 8B450C   mov eax,[ebp+$0c] 
00B972CB 8B78FC   mov edi,[eax-$04] 
00B972CE 8BC6    mov eax,esi 
00B972D0 E88F559BFF  call TBitmap.GetWidth 
... 

並與全球功能:

function drawBitmap(const Canvas: TCanvas; const ARect: TRectF; const aBitmap: {$IFDEF _USE_TEXTURE}TTexture{$ELSE}Tbitmap{$ENDIF}; const aTopLeft: TpointF; Const aOpacity: Single): boolean; inline; 
var aDestRect: TrectF; 
begin 
    Result := False; 
    if aBitmap <> nil then begin 

    //calculate aDestRect 
    aDestRect := canvas.AlignToPixel(
        TRectF.Create(
         aTopLeft, 
         aBitmap.Width/ScreenScale, 
         aBitmap.Height/ScreenScale)); 

    //if the aBitmap is visible 
    if ARect.IntersectsWith(aDestRect) then begin 

     Result := True; 

     {$IFDEF _USE_TEXTURE} 
     TCustomCanvasGpu(Canvas).DrawTexture(aDestRect, // ATexRect 
              TRectF.Create(0, 
                 0, 
                 aBitmap.Width, 
                 aBitmap.Height), // ARect 
              ALPrepareColor(TCustomCanvasGpu.ModulateColor, aOpacity * AbsoluteOpacity), // https://quality.embarcadero.com/browse/RSP-15432 
              aBitmap); 
     {$ELSE} 
     Canvas.DrawBitmap(aBitmap, // ABitmap 
         TRectF.Create(0, 
             0, 
             aBitmap.Width, 
             aBitmap.Height), // SrcRect 
         aDestRect, // DstRect 
         aOpacity * AbsoluteOpacity, // AOpacity 
         samevalue(aDestRect.Width, aBitmap.Width, Tepsilon.Position) and 
         samevalue(aDestRect.height, aBitmap.height, Tepsilon.Position)); // HighSpeed - set interpolation to none 
     {$ENDIF}; 

    end; 

    end; 
end; 

procedure TMyObject.onPaint(Sender: TObject; Canvas: TCanvas; const ARect: TRectF); 
begin 

    drawBitmap(aBitmap, aPos, 1); 

end; 

ASM:

MyObject.pas.2636: drawBitmap(Canvas, aRect, fBtnFilterBitmap, // aBitmap 
00B98F6D 8BFB    mov edi,ebx 
00B98F6F 8B83A8040000  mov eax,[ebx+$000004a8] 
00B98F75 8945F0   mov [ebp-$10],eax 
00B98F78 8D83C4050000  lea eax,[ebx+$000005c4] 
00B98F7E 8945EC   mov [ebp-$14],eax 
00B98F81 C645EB00   mov byte ptr [ebp-$15],$00 
00B98F85 8B75F0   mov esi,[ebp-$10] 
00B98F88 85F6    test esi,esi 
00B98F8A 0F840A020000  jz $00b9919a 
00B98F90 8BC6    mov eax,esi 
00B98F92 E8CD389BFF  call TBitmap.GetWidth 
... 
+1

查找「不要使用嵌套的例程」[here](http://web.archive.org/web/20020406005808/http://www.optimalcode.com:80/general.htm)指南。 –

+1

Quote:「嵌套的例程(其他例程中的例程;也稱爲」本地過程「)需要一些特殊的堆棧操作,以便外部例程的變量可以被內部例程看到,這會導致很大的開銷。除了嵌套之外,將過程移至單元範圍級別並傳遞必要的變量 - 如果需要,可以通過引用(使用var關鍵字) - 或者在單位範圍內創建變量global。「 –

+0

@Uli我打電話給你。否則我的經驗說。你不能認真對待使用全局變量嗎?如何在沒有分析的情況下支持一攬子建議? –

回答

5

可以肯定的唯一方式就是以描繪兩種變體的性能。

這就是說,看着代碼,我的直覺告訴我,你將無法測量兩個變體之間的任何顯着差異。性能將取決於被調用的系統函數,而不是您的高級代碼。但是,這只是一個受過教育的猜測。比較兩者和衡量。

就你使用內聯而言,我對此有些懷疑。這是一個相當大的功能,並且內襯對於短葉功能而言往往是最有效的。再一次,我懷疑這裏的內聯會改變什麼,但我的直覺說,如果有的話,這裏的內聯更有可能阻礙性能而不是幫助。

要強調的另一件事是,你應該專注於優化瓶頸。你是否檢查過這個功能是瓶頸?如果不是,那麼任何優化工作都會浪費。

6

這裏,關於使用VCL TCanvas,調用該函數將立即進行。所以顯然是不成熟的優化,兩者在實踐上沒有性能差異。全局函數可能更難以維護(除非它是一些代碼,可以在單元的其他地方實際重用)。無論如何,即使是全局函數也不是一個好主意:如果你有一些特定的可重用過程,定義一個class而不是:它將更清晰,更容易調試/擴展/測試。

只適用於非常小的函數,它不會調用任何其他函數,內聯可能會帶來一些性能優勢。例如:

function Add(n1,n2: integer): integer; inline; 
begin 
    result := n1 + n2; 
end; 

但在你的情況下,它沒有任何意義。

而且,正如您所說的那樣,編譯器需要實際內聯asm,否則。如果它聲明內聯不會產生任何好處(它甚至可能比子函數慢),它不會內聯函數。

爲了完整性,在低assassimp級別,當您在另一個函數中調用本地函數時,訪問作用域中的變量時添加調用者「堆棧幀」指針作爲附加參數。

在僞代碼,它是這樣的:

function _drawBitmap(const stackframe: TLocalStackRecord; const aBitmap: {$IFDEF _USE_TEXTURE}TTexture{$ELSE}Tbitmap{$ENDIF}; const aTopLeft: TpointF; Const aOpacity: Single): boolean; 
    var aDestRect: TrectF; 
    begin 
    Result := False; 
    if aBitmap <> nil then begin 

     //calculate aDestRect 
     aDestRect := stackframe.canvas.AlignToPixel(
        TRectF.Create(
         aTopLeft, 
         aBitmap.Width/ScreenScale, 
         aBitmap.Height/ScreenScale)); 
    ... 

儘量避免過早的優化:

程序員浪費大量的時間思考,也不必擔心 約,非關鍵性的速度部分程序和這些 在考慮調試和維護時實際上對效率的嘗試實際上具有強烈的負面影響。我們應該忘記效率的大小,比如97%的時間:不成熟的優化是所有邪惡的根源。然而,我們不應該放棄我們在這個關鍵3%的機會。 Knuth中的變體,「使用Goto語句進行結構化編程」。計算調查6:4(1974年12月),第261-301頁,§1。

爲了避免浪費你的時間(和金錢),使用一個探查器 - 例如, Eric's Sampling Profiler - 找出哪部分代碼實際需要優化。

說得對,然後快點。並使其始終可收回和可維護。

+0

它是FMX代碼而不是VCL,雖然結論是一樣的 –

+0

謝謝arnaud!我更新了一點我的問題,並添加了2 v的asm正如你所看到的那樣,全局內​​聯函數使用比本地函數少得多的代碼。我100%同意你的建議,擔心他們程序中非關鍵部分的速度...... – andrey

+0

難以理解該評論。您是否希望代碼在iOS/Android上快速運行,還是希望儘量減少編譯的x86代碼的大小? –