25

我正在将一些应用程序从 32 位移植到 64 位 delphi,这些应用程序进行了大量的文本处理,并注意到处理速度发生了极大的变化。使用一些程序进行了一些测试,例如,在 64 位中,这已经比编译到 32 位花费了 200% 以上的时间(2000+ ms 与 ~900 相比)

这是正常的吗?

function IsStrANumber(const S: AnsiString): Boolean;
var P: PAnsiChar;
begin
  Result := False;
  P := PAnsiChar(S);
  while P^ <> #0 do begin
    if not (P^ in ['0'..'9']) then Exit;
    Inc(P);
  end;
  Result := True;
end;

procedure TForm11.Button1Click(Sender: TObject);
Const x = '1234567890';
Var a,y,z: Integer;
begin
  z := GetTickCount;
  for a := 1 to 99999999 do begin
   if IsStrANumber(x) then y := 0;//StrToInt(x);
  end;
  Caption := IntToStr(GetTickCount-z);
end;
4

6 回答 6

35

目前没有解决方案,因为大多数 64 位字符串例程的代码都是用PUREPASCAL定义的 IOW 编译的,它是纯 Delphi,没有汇编程序,而许多重要的代码32 位字符串例程由 FastCode 项目和汇编程序完成。

目前,没有 64 位的 FastCode 等价物,我假设开发团队无论如何都会尝试消除汇编程序,特别是因为他们正在转向更多平台。

这意味着生成代码的优化变得越来越重要。我希望宣布的向 LLVM 后端的迁移将大大加快大部分代码的速度,因此纯 Delphi 代码不再是这样的问题。

很抱歉,没有解决方案,但也许是一个解释。

更新

从 XE4 开始,相当多的 FastCode 例程已经取代了我在上面段落中谈到的未优化的例程。它们通常仍然是PUREPASCAL,但它们代表了一个很好的优化。所以情况没有以前那么糟糕了。和普通字符串例程在OS XTStringHelper中仍然显示一些错误和一些极慢的代码(特别是在从 Unicode 转换到 Ansi 或反之亦然的情况下),但 RTL 的Win64部分似乎要好得多。

于 2012-06-29T11:48:42.710 回答
6

尽量避免在循环中分配任何字符串。

在您的情况下,可能涉及 x64 调用约定的堆栈准备。您是否尝试将其IsStrANumber声明为inline

我想这会让它更快。

function IsStrANumber(P: PAnsiChar): Boolean; inline;
begin
  Result := False;
  if P=nil then exit;
  while P^ <> #0 do
    if not (P^ in ['0'..'9']) then 
      Exit else
      Inc(P);
  Result := True;
end;

procedure TForm11.Button1Click(Sender: TObject);
Const x = '1234567890';
Var a,y,z: Integer;
    s: AnsiString;
begin
  z := GetTickCount;
  s := x;
  for a := 1 to 99999999 do begin
   if IsStrANumber(pointer(s)) then y := 0;//StrToInt(x);
  end;
  Caption := IntToStr(GetTickCount-z);
end;

RTL的“纯帕斯卡”版本确实是这里缓慢的原因......

请注意,与 32 位版本相比,使用 FPC 64 位编译器的情况更糟……听起来 Delphi 编译器不是唯一的!不管市场怎么说,64 位并不意味着“更快”!有时甚至是相反的(例如,众所周知,JRE 在 64 位上速度较慢,并且在涉及指针大小时将在 Linux 中引入新的x32 模型)。

于 2012-06-29T15:40:05.393 回答
5

代码可以这样编写,具有良好的性能结果:

function IsStrANumber(const S: AnsiString): Boolean; inline;
var
  P: PAnsiChar;
begin
  Result := False;
  P := PAnsiChar(S);
  while True do
  begin
    case PByte(P)^ of
      0: Break;
      $30..$39: Inc(P);
    else
      Exit;
    end;
  end;
  Result := True;
end;

Intel(R) Core(TM)2 CPU T5600 @ 1.83GHz

  • x32 位:2730 毫秒
  • x64 位:3260 毫秒

Intel(R) Pentium(R) D CPU 3.40GHz

  • x32 位:2979 毫秒
  • x64 位:1794 毫秒

展开上述循环可以加快执行速度:

function IsStrANumber(const S: AnsiString): Boolean; inline; 
type
  TStrData = packed record
    A: Byte;
    B: Byte;
    C: Byte;
    D: Byte;
    E: Byte;
    F: Byte;
    G: Byte;
    H: Byte;
  end;
  PStrData = ^TStrData;
var
  P: PStrData;
begin
  Result := False;
  P := PStrData(PAnsiChar(S));
  while True do
  begin
    case P^.A of
      0: Break;
      $30..$39:
        case P^.B of
          0: Break;
          $30..$39:
            case P^.C of
              0: Break;
              $30..$39:
                case P^.D of
                  0: Break;
                  $30..$39:
                    case P^.E of
                      0: Break;
                      $30..$39:
                        case P^.F of
                          0: Break;
                          $30..$39:
                            case P^.G of
                              0: Break;
                              $30..$39:
                                case P^.H of
                                  0: Break;
                                  $30..$39: Inc(P);
                                else
                                  Exit;
                                end;
                            else
                              Exit;
                            end;
                        else
                          Exit;
                        end;
                    else
                      Exit;
                    end;
                else
                  Exit;
                end;
            else
              Exit;
            end;
        else
          Exit;
        end;
    else
      Exit;
    end;
  end;
  Result := True;
end;

Intel(R) Core(TM)2 CPU T5600 @ 1.83GHz

  • x32 位:2199 毫秒
  • x64 位:1934 毫秒

Intel(R) Pentium(R) D CPU 3.40GHz

  • x32 位:1170 毫秒
  • x64 位:1279 毫秒

如果你也应用 Arnaud Bouchez 所说的,你可以让它更快。

于 2012-06-29T22:25:35.643 回答
2

p^ in ['0'..'9']64 位的测试很慢。

添加了一个内联函数,其中包含对下/上边界的测试而不是in []测试,以及对空字符串的测试。

function IsStrANumber(const S: AnsiString): Boolean; inline;
var
  P: PAnsiChar;
begin
  Result := False;
  P := Pointer(S);
  if (P = nil) then
    Exit;
  while P^ <> #0 do begin
    if (P^ < '0') then Exit;
    if (P^ > '9') then Exit;
    Inc(P);
  end;
  Result := True;
end;

基准测试结果:

        x32     x64
--------------------
hikari  1420    3963
LU RD   1029    1060

在 32 位中,主要的速度差异是内联,它P := PAnsiChar(S);会在分配指针值之前调用外部 RTL 例程进行 nil 检查,而P := Pointer(S);只是分配指针。

观察到这里的目标是测试字符串是否为数字然后转换它,为什么不使用 RTL TryStrToInt(),它一步完成并处理符号和空格。

通常在分析和优化例程时,最重要的是找到解决问题的正确方法。

于 2014-02-22T00:03:39.397 回答
1

64 位的好处在于地址空间,而不是速度(除非您的代码受到可寻址内存的限制)。

从历史上看,这种字符操作代码在更广泛的机器上总是较慢。从 16 位 8088/8086 迁移到 32 位 386 确实如此。将 8 位字符放入 64 位寄存器是对内存带宽和缓存的浪费。

为了速度,您可以避免使用 char 变量、使用指针、使用查找表、使用位并行(在一个 64 位字中处理 8 个字符)或使用 SSE/SSE2... 指令。显然,其中一些会使您的代码依赖 CPUID。此外,在调试时打开 CPU 窗口,并寻找编译器“为”你喜欢无声字符串转换(尤其是在调用周围)做愚蠢的事情。

您可以尝试查看 FastCode 库中的一些本机 Pascal 例程。EG PosEx_Sha_Pas_2 虽然不如汇编版本快,但比 RTL 代码(32 位)快。

于 2012-06-30T06:58:21.687 回答
1

这里有两个功能。一个只检查正数。第二个检查是否为阴性。并且不限于大小。第二个比常规快 4 倍Val

function IsInteger1(const S: String): Boolean; overload;
var
  E: Integer;
  Value: Integer;
begin
  Val(S, Value, E);
  Result := E = 0;
end;


function IsInteger2(const S: String): Boolean; inline; 
var
    I: Integer;
begin
    Result := False;
    I := 0;
  while True do
  begin
    case Ord(S[I+1]) of
      0: Break;
      $30..$39:
        case Ord(S[I+2]) of
          0: Break;
          $30..$39:
            case Ord(S[I+3]) of
              0: Break;
              $30..$39:
                case Ord(S[I+4]) of
                  0: Break;
                  $30..$39:
                    case Ord(S[I+5]) of
                      0: Break;
                      $30..$39:
                        case Ord(S[I+6]) of
                          0: Break;
                          $30..$39:
                            case Ord(S[I+7]) of
                              0: Break;
                              $30..$39:
                                case Ord(S[I+8]) of
                                  0: Break;
                                  $30..$39:
                                    case Ord(S[I+9]) of
                                      0: Break;
                                      $30..$39: 
                                        case Ord(S[I+10]) of
                                          0: Break;
                                          $30..$39: Inc(I, 10);
                                        else
                                          Exit;
                                        end;
                                    else
                                      Exit;
                                    end;
                                else
                                  Exit;
                                end;
                            else
                              Exit;
                            end;
                        else
                          Exit;
                        end;
                    else
                      Exit;
                    end;
                else
                  Exit;
                end;
            else
              Exit;
            end;
        else
          Exit;
        end;
    else
      Exit;
    end;
  end;
  Result := True;
end;

function IsInteger3(const S: String): Boolean; inline;
var
  I: Integer;
begin
  Result := False;
  case Ord(S[1]) of
    $2D,
    $30 .. $39:
    begin
      I := 1;
      while True do
      case Ord(S[I + 1]) of
        0:
        Break;
        $30 .. $39:
        case Ord(S[I + 2]) of
          0:
          Break;
          $30 .. $39:
          case Ord(S[I + 3]) of
            0:
            Break;
            $30 .. $39:
            case Ord(S[I + 4]) of
              0:
              Break;
              $30 .. $39:
              case Ord(S[I + 5]) of
                0:
                Break;
                $30 .. $39:
                case Ord(S[I + 6]) of
                  0:
                  Break;
                  $30 .. $39:
                  case Ord(S[I + 7]) of
                    0:
                    Break;
                    $30 .. $39:
                    case Ord(S[I + 8]) of
                      0:
                      Break;
                      $30 .. $39:
                      case Ord(S[I + 9]) of
                        0:
                        Break;
                        $30 .. $39:
                        case Ord(S[I + 10]) of
                          0:
                          Break;
                          $30 .. $39:
                          case Ord(S[I + 11]) of
                            0:
                            Break;
                            $30 .. $39:
                            case Ord(S[I + 12]) of
                              0:
                              Break;
                              $30 .. $39:
                              case Ord(S[I + 13]) of
                                0:
                                Break;
                                $30 .. $39:
                                Inc(I, 13);
                              else
                                Exit;
                              end; 
                            else
                              Exit;
                            end; 
                          else
                            Exit;
                          end; 
                        else
                          Exit;
                        end; 
                      else
                        Exit;
                      end; 
                    else
                      Exit;
                    end; 
                  else
                    Exit;
                  end; 
                else
                  Exit;
                end; 
              else
                Exit;
              end;  
            else
              Exit;
            end;  
          else
            Exit;
          end;   
        else
          Exit;
        end;    
      else
        Exit;
      end;
    end;
  else
    Exit;
  end;
  Result := True;
end;
于 2014-02-21T13:40:37.997 回答