是否更优化使用本地功能或全局功能?

and*_*rey 2 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;  
Run Code Online (Sandbox Code Playgroud)

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
...
Run Code Online (Sandbox Code Playgroud)

并具有全球功能:

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;  
Run Code Online (Sandbox Code Playgroud)

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
...
Run Code Online (Sandbox Code Playgroud)

Arn*_*hez 6

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

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

function Add(n1,n2: integer): integer; inline;
begin
  result := n1 + n2;
end;
Run Code Online (Sandbox Code Playgroud)

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

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

为了完整性,在低组合级别,当您在另一个函数中调用本地函数时,访问范围中的变量将添加调用者"堆栈帧"指针作为附加参数.

在伪代码中,它是这样的:

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));
  ...
Run Code Online (Sandbox Code Playgroud)

尽量避免过早优化:

程序员浪费了大量时间来思考或担心程序中非关键部分的速度,而这些效率尝试实际上在考虑调试和维护时会产生很大的负面影响.我们应该忘记小的效率,大约97%的时间说:过早的优化是所有邪恶的根源.然而,我们不应该放弃那个关键的3%的机会. Knuth中的变体,"使用Goto语句进行结构化编程".计算机调查6:4(1974年12月),第261-301页,§1.

为避免浪费您的时间(和金钱),请使用分析器(例如Eric的采样分析器)来确定实际需要优化代码的哪一部分.

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


Dav*_*nan 5

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

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

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

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