Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Performance 如何加速此转换功能?_Performance_Delphi_Loops - Fatal编程技术网

Performance 如何加速此转换功能?

Performance 如何加速此转换功能?,performance,delphi,loops,Performance,Delphi,Loops,我有一个名为IntToStrLen的函数,它将整数转换为填充大小的字符串。例如,大小为4的整数值42将产生一个字符串\uu 42(默认情况下用给定字符填充) 问题是,当批量使用此函数时(例如,在循环中使用1000000次),它会给循环增加额外的重量。我正在使用的循环,没有这个函数,大约需要20秒,但是有了这个函数,我现在仍然在等待这个函数完成,大约5分钟后 如何加速以下功能 function IntToStrLen(const Value: Integer; const Len: Integer

我有一个名为
IntToStrLen
的函数,它将整数转换为填充大小的字符串。例如,大小为
4
的整数值
42
将产生一个字符串
\uu 42
(默认情况下用给定字符填充)

问题是,当批量使用此函数时(例如,在循环中使用1000000次),它会给循环增加额外的重量。我正在使用的循环,没有这个函数,大约需要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;
函数IntToStrLen(常量值:整数;常量长度:整数;
常量填充:字符串=“”):字符串;
变量
T:弦;
开始
结果:=IntToStr(值)//转换结果
如果长度(结果)>Len,则
结果:=复制(结果,1,Len)//强制截断
否则,如果长度(结果)
试试这个变体
(并且不处理负数)

函数IntToStrLen(常量值:整数;常量长度:整数;
常量填充:Char='':字符串;
变量
T:弦;
开始
结果:=IntToStr(值);
如果长度(结果)>Len,则
SetLength(Result,Len)//强制截断
否则,如果长度(结果)

p.S.如此奇怪的截断(2014=>20)-是您真正想要的吗?

这里可以做的第一个绝对改变是避免堆分配。您的代码目前有多个堆分配。这里的目标是编写一个堆分配为零的函数

这意味着您需要调用方分配并提供缓冲区。通常,它们会使用堆栈分配的缓冲区来完成,因此分配(或取消分配)不会产生任何成本。如果调用者确实需要一个总是在堆上分配的
字符串
,那么调用者可以通过调用
SetString
来包装函数

函数原型可能如下所示:

procedure IntToStrLen(const Value, Len: Integer; var Buffer: array of Char;
  const Fill: Char = ' ');
这里要强调的第一点是
Fill
必须是
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;
此时,您将获得大部分可用的性能优势。从现在起,你将进入收益递减的状态。您可以像在
SysUtils中那样对
CopyIntegerToCharBuffer
进行微优化。除此之外,我相信明智地使用汇编程序可以优化
IntToStrLen
的实现。但这样的优化不会带来任何好处,就像您迄今为止从避免堆中获得的好处一样


当然,所有这些都假设您已经正确识别了性能瓶颈。通过静态分析代码,很容易假设您知道性能瓶颈在哪里。除非您确实仔细地分析了它,否则您可能会发现您的直觉对在何处投资优化工作的判断很差。

仍然需要一段时间,但我需要创建一个测试场来记录速度-因为我当前的测试正在加载/读取一个大的二进制文件。它将一个20mb文件中的每个字节转换成一个字符串并显示在字节网格中。显示一个大表可能需要很多时间。谁想看2000万个数字呢?就其实现的性质而言,结果必须是精确指定的长度,截断它当然不优雅,但这是确保它是完美长度的唯一方法。据你所知,我可能正在设计矩阵:-)我已经测试过了。在旧Athlon处理器上,对于Len=10和2000万个数字,我们的函数可以工作5-20秒(只有运算符SetLength(Result)的空函数可以工作2秒),所以在您的程序FWIW中还有另一个计时程序,您的代码在读取之前不会初始化
T
。@DavidHeffernan字符串由您初始化know@JerryDodge我在所有场景中都会初始化托管类型的局部变量,这就是我们在这里讨论的,对吗?@StefanGlienke
function GetText:String;开始结果:=结果+文本;结束然后
;结束应说明为何应初始化函数结果。函数的结果永远不会被重置为空白,因此它会不断添加到同一个字符串中。@Jerrydoge我很清楚这个问题-我建议您投票支持天文改进,非常感谢:-)在这里使用短字符串作为函数的返回值不是更容易吗?还是我错过了一些会让事情变慢的东西+1用于考虑非英特尔处理器。@Johan一个短字符串可能会有用。但是接下来需要将8位文本编码转换为16位文本编码。我没有考虑非英特尔处理器。您需要为ARM编写一个新的
DivMod
@DavidHeffernan是否有一些避免堆分配的一般指导原则?@user3060326我要说的第一件事是,如果有明显的好处,只能尝试我答案中的代码。它确实使代码不可用
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;
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;