是否更优化使用本地函数或全局函数?

时间:2017-08-29 06:01:55

标签: delphi

我想知道是否更优化使用本地功能(在_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
...

2 个答案:

答案 0 :(得分:6)

这里,就使用VCL TCanvas而言,立即调用该函数。因此,显然过早优化,两者之间在实践中没有性能差异。全局函数可能更难维护(除非它是一些可以在单元中的其他地方实际重用的代码)。无论如何,即使是全局函数也不是一个好主意:如果你有一些特定的可重用过程,那么定义一个class:它将更干净,更容易调试/扩展/测试。

仅适用于不调用任何其他功能的非常小的功能,内联可能会带来一些性能优势。例如:

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

但在你的情况下,它没有任何意义。

并且,正如您所说,编译器实际上是否内联asm。如果它声明内联不会带来任何好处(它甚至可能比子函数慢),它就不会内联函数。

为了完整性,在低组合级别,当你在另一个函数中调用本地函数时,访问范围中的变量就完成了添加调用者“stack frame”指针作为附加参数。

在伪代码中,就像那样:

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 - 找出实际需要优化代码的哪一部分。

做对,然后快点。并使其始终可以重复使用和维护。

答案 1 :(得分:5)

唯一可以确定的方法是分析两种变体的性能。

那就是说,看着代码,我的直觉告诉我你将无法衡量两种变体之间的任何显着差异。性能将由调用的系统函数决定,而不是由高级代码决定。但是,这只是一个有根据的猜测。比较两者并测量。

就你对inline的使用而言,我在这里有点怀疑。这是一个相当大的功能,内联往往是最有效的短叶功能。同样,我怀疑这里的内联会改变什么,但我的直觉说,如果有的话,在这里内联更有可能阻碍性能而不是帮助。

另一件需要强调的是,您应该首先集中精力优化瓶颈。您是否检查过此功能是否存在瓶颈?如果不是,则浪费任何优化工作。