我使用的是 Delphi 2009。在我的程序中,我一直在非常努力地优化我所有的 Delphi 代码以提高速度和内存使用,尤其是我的 Unicode 字符串处理。
我有以下声明:
Result := Result + GetFirstLastName(IndiID, 1);
当我调试该行时,从 GetFirstLastName 函数返回时,它跟踪到系统单元中的例程 _UStrArrayClr:
procedure _UStrArrayClr(var StrArray; Count: Integer);
asm
JMP _LStrArrayClr
end;
这会调用 _LStrArrayClr:
procedure _LStrArrayClr(var StrArray; cnt: longint);
{$IFDEF PUREPASCAL}
var
P: Pointer;
begin
P := @StrArray;
while cnt > 0 do
begin
_LStrClr(P^);
Dec(cnt);
Inc(Integer(P), sizeof(Pointer));
end;
end;
{$ELSE}
asm
{ -> EAX pointer to str }
{ EDX cnt }
PUSH EBX
PUSH ESI
MOV EBX,EAX
MOV ESI,EDX
@@loop:
MOV EDX,[EBX] { fetch str }
TEST EDX,EDX { if nil, nothing to do }
JE @@doneEntry
MOV dword ptr [EBX],0 { clear str }
MOV ECX,[EDX-skew].StrRec.refCnt { fetch refCnt }
DEC ECX { if < 0: literal str }
JL @@doneEntry
LOCK DEC [EDX-skew].StrRec.refCnt { threadsafe dec refCount }
JNE @@doneEntry
LEA EAX,[EDX-skew].StrRec.codePage { if refCnt now zero, deallocate}
CALL _FreeMem
@@doneEntry:
ADD EBX,4
DEC ESI
JNE @@loop
POP ESI
POP EBX
end;
{$ENDIF}
并为每个字符运行一次循环,并在从那里退出时调用 _UStrCat:
procedure _UStrCat(var Dest: UnicodeString; const Source: UnicodeString);
asm
{ -> EAX pointer to dest }
{ EDX source }
TEST EDX,EDX // Source empty, nop.
JE @@exit
MOV ECX,[EAX] // ECX := Dest
TEST ECX,ECX // Nil source => assignment
JE _UStrAsg
PUSH EBX
PUSH ESI
PUSH EDI
MOV EBX,EAX // EBX := @Dest
MOV ESI,EDX // ESI := Source
CMP ESI,ECX
JE @@appendSelf
CMP [ECX-skew].StrRec.elemSize,2
JE @@destIsUnicode
CALL _EnsureUnicodeString
MOV EDI,EAX
MOV ECX,EAX
@@destIsUnicode:
PUSH 0
CMP [ESI-skew].StrRec.elemSize,2
JE @@sourceIsUnicode
MOV EDI,ECX
MOV EAX,ESI
MOV [ESP],ESI
CALL _UStrAddRef
MOV EAX,ESP
CALL _EnsureUnicodeString
MOV ESI,[ESP]
MOV ECX,EDI
@@sourceIsUnicode:
MOV EDI,[ECX-skew].StrRec.length // EDI := Length(Dest)
MOV EDX,[ESI-skew].StrRec.length // EDX := Length(Source)
ADD EDX,EDI // EDX := (Length(Source) + Length(Dest)) * 2
TEST EDX,$C0000000
JNZ @@lengthOverflow
MOV EAX,EBX
CALL _UStrSetLength // Set length of Dest
MOV EAX,ESI // EAX := Source
MOV ECX,[ESI-skew].StrRec.length // ECX := Length(Source)
@@noTemp:
MOV EDX,[EBX] // EDX := Dest
SHL EDI,1 // EDI to bytes (Length(Dest) * 2)
ADD EDX,EDI // Offset EDX for destination of move
SHL ECX,1 // convert Length(Source) to bytes
CALL Move // Move(Source, Dest + Length(Dest)*2, Length(Source)*2)
MOV EAX,ESP // Need to clear out the temp we may have created above
MOV EDX,[EAX]
TEST EDX,EDX
JE @@tempEmpty
CALL _LStrClr
@@tempEmpty:
POP EAX
POP EDI
POP ESI
POP EBX
RET
@@appendSelf:
CMP [ECX-skew].StrRec.elemSize,2
JE @@selfIsUnicode
MOV EAX,EBX
XOR EDX,EDX
CALL _EnsureUnicodeString
MOV ECX,EAX
MOV EAX,EBX
@@selfIsUnicode:
MOV EDI,[ECX-skew].StrRec.length
MOV EDX,EDI
SHL EDX,1
TEST EDX,$C0000000
JNZ @@lengthOverflow
CALL _UStrSetLength
MOV EAX,[EBX]
MOV ECX,EDI
PUSH 0
JMP @@noTemp
@@lengthOverflow:
JMP _IntOver
@@exit:
end;
并贯穿整个例程。
我的“结果”是一个字符串,因此是 Unicode。我的 GetFirstLastName 返回一个字符串,它是 Unicode。不需要转换字符集。
我无法真正说明这些系统过程在做什么,但它们给我的例行程序增加了很多开销。
他们在做什么?它们有必要吗?如果它们不是必需的,我怎样才能阻止编译器调用这些例程?
最佳答案
LStrArrayClear 不会对每个字符运行一次循环;它为数组中的每个字符串运行一次,以减少引用计数并在它命中 0 时释放该字符串。这是由编译器插入的,用于清理分配为局部变量的任何字符串,或它创建的用于保存结果的任何临时字符串连接两个字符串。
UStrCat 是字符串连接例程。这就是 string1 + string2
在幕后的翻译。编译器确定它应该产生一个 Unicode 字符串,所以它接受两个输入字符串,测试它们本身是否是 Unicode,如果不是则转换它们(但你的是,所以转换得到skipped,) 然后设置结果的大小并复制数据。
UStrCat 是必需的,对此您无能为力。 LStrArrayClear 是事情变得有点模糊的地方。当您创建一个使用字符串的例程时,编译器必须分配足够的临时字符串来处理您可以在其中执行的所有操作,无论您是否做过。然后它必须在之后清除它们。因此,通过将不常见的任务移至其他函数来减少不必要的字符串操作会有所帮助,尤其是在紧密循环中。
例如,您多久看到一次这样的事情?
if SomethingIsVeryWrong then
raise ETimeToPanic.Create('Everybody panic! File ' + filename + ' is corrupt at address ' + intToStr(FailureAddress) + '!!!');
此错误消息包含 5 个不同的子字符串。即使它设法通过重用它们来优化事物,它仍然需要分配至少两个临时字符串才能使其工作。假设这是在一个紧密循环内发生的,并且您不希望此错误频繁发生(如果有的话)。您可以通过将字符串连接卸载到格式调用中来消除临时字符串。这是一个非常方便的优化,事实上,它内置于 Exception
中。
if SomethingIsVeryWrong then
raise ETimeToPanic.CreateFmt('Everybody panic! File %s is corrupt at address %d!!!', [filename, FailureAddress]);
是的,对 Format 的调用将比直接连接运行得慢得多,但如果出现问题,它只会运行一次,而且性能是您最不担心的。
关于delphi - 我可以消除额外的 Unicode 字符串调用吗(Delphi),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3137107/