Jer*_*dge 2 delphi performance loops
我有这个函数调用IntToStrLen,它将一个整数转换为填充大小的字符串.例如,42大小为的整数值4将导致字符串__42(使用给定字符填充,默认为空格).
问题是当这个函数被批量使用时(例如循环中的1,000,000次),它会给循环增加额外的重量.我正在使用它的循环,没有这个功能,大约需要20秒,但是使用这个功能,我现在还在等待大约5分钟后完成此功能.
如何加快以下功能?
function IntToStrLen(const Value: Integer; const Len: Integer;
const Fill: String = ' '): String;
var
T: String;
begin
Result:= IntToStr(Value); //convert result
if Length(Result) > Len then
Result:= Copy(Result, 1, Len) //forcefully truncate
else if Length(Result) < Len then begin
T:= '';
while Length(T) < (Len - Length(Result)) do //fill space with character
T:= T + Fill;
Result:= T + Result; //return combination
end;
end;
Run Code Online (Sandbox Code Playgroud)
您可以在此处进行的绝对第一项更改是避免堆分配.您的代码目前有多个堆分配.您的目标是编写零堆分配的函数.
这意味着您需要调用者来分配和提供缓冲区.通常他们会使用堆栈分配的缓冲区来执行此操作,因此无需分配(或解除分配)任何成本.如果调用者确实需要a string,它总是在堆上分配,那么调用者可以通过调用来包装你的函数SetString.
函数原型可能如下所示:
procedure IntToStrLen(const Value, Len: Integer; var Buffer: array of Char;
const Fill: Char = ' ');
Run Code Online (Sandbox Code Playgroud)
这里要强调的第一点是Fill必须是a Char.您的使用string效率低下,并允许调用者调用长度不等于1的填充"字符".这样做当然会破坏你的功能,因为它会返回一个长度不等于的值Len.
请注意,实现不得调用,IntToStr因为这涉及堆分配.因此,您需要将自己的堆分配空闲整数写入十进制文本转换代码,因为令人惊讶的是,RTL不提供此类功能.当我这样做时,我使用这样的代码:
procedure DivMod(Dividend, Divisor: Cardinal;
out Quotient, Remainder: Cardinal);
{$IFDEF CPUX86}
asm
PUSH EBX
MOV EBX,EDX
XOR EDX,EDX
DIV EBX
MOV [ECX],EAX
MOV EBX,Remainder
MOV [EBX],EDX
POP EBX
end;
{$ELSE IF Defined(CPUX64)}
asm
.NOFRAME
MOV EAX,ECX
MOV ECX,EDX
XOR EDX,EDX
DIV ECX
MOV [R8],EAX
MOV [R9],EDX
end;
{$ELSE}
{$Message Error 'Unrecognised platform.'}
{$ENDIF}
function CopyIntegerToCharBuffer(const Value: Integer;
var Buffer: array of Char): Integer;
var
i, j: Integer;
val, remainder: Cardinal;
negative: Boolean;
tmp: array [0..15] of Char;
begin
negative := Value<0;
val := abs(Value);
Result := 0;
repeat
DivMod(val, 10, val, remainder);
tmp[Result] := Chr(remainder + ord('0'));
inc(Result);
until val=0;
if negative then begin
tmp[Result] := '-';
inc(Result);
end;
Assert(Result<=Length(Buffer));
i := 0;
j := Result-1;
while i<Result do begin
Buffer[i] := tmp[j];
inc(i);
dec(j);
end;
end;
Run Code Online (Sandbox Code Playgroud)
现在,您可以在不触及堆的情况下对整数进行十进制文本表示.从那里开始,这是一个简短的功能.
procedure IntToStrLen(const Value, Len: Integer; var Buffer: array of Char;
const Fill: Char = ' ');
var
tmp: array [0..15] of Char;
i, N: Integer;
begin
Assert(Length(Buffer)>=Len);
N := CopyIntegerToCharBuffer(Value, tmp);
if N>=Len then begin
Move(tmp, Buffer, SizeOf(Char)*Len);
end else begin
for i := 0 to Len-N-1 do begin
Buffer[i] := Fill;
end;
Move(tmp, Buffer[Len-N], SizeOf(Char)*N);
end;
end;
Run Code Online (Sandbox Code Playgroud)
此时,您将获得大部分可用的性能优势.从现在开始,你将会收益递减.你可以微优化CopyIntegerToCharBuffer是在做SysUtils._IntToStr32了实例.除此之外,我确信IntToStrLen可以通过明智地使用汇编程序来优化实现.但是这样的优化不会产生你迄今为止从避免堆中获得的好处.
当然,所有这些都假设您已正确识别性能瓶颈.通过静态分析代码,您可以轻松地假设您知道性能瓶颈在哪里.除非你真的对它进行了详细的描述,否则你会发现你的直觉对于在哪里投入优化工作是一个糟糕的判断.