我想知道是否更优化使用本地功能(在_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
...
答案 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的使用而言,我在这里有点怀疑。这是一个相当大的功能,内联往往是最有效的短叶功能。同样,我怀疑这里的内联会改变什么,但我的直觉说,如果有的话,在这里内联更有可能阻碍性能而不是帮助。
另一件需要强调的是,您应该首先集中精力优化瓶颈。您是否检查过此功能是否存在瓶颈?如果不是,则浪费任何优化工作。