为什么Delphi zlib和zip库在64位之下这么慢?

虽然对现实世界的应用程序进行了基准测试,但我遇到了与Delphi一起提供的zlib和zip库的令人惊讶的性能特征.

我的真实应用程序导出.xlsx文件.这个文件格式是一个包含在ZIP容器文件中的XML文件的集合. .xlsx导出代码生成XML文件,然后将其提供给Delphi ZIP库.一旦我优化了XML文件生成到ZIP创建是我发现的瓶颈,我感到惊讶的是,这个64位代码比32位代码慢得多.

为了进一步学习,我创建了这个测试程序:

program zlib_perf;

{$APPTYPE CONSOLE}

uses
  System.SysUtils,System.Classes,System.Diagnostics,System.Zip;

const
  LoremIpsum =
    'Lorem ipsum dolor sit amet,consectetur adipiscing elit,sed do eiusmod '+
    'tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,'+
    'quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo '+
    'consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse '+
    'cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat '+
    'non proident,sunt in culpa qui officia deserunt mollit anim id est laborum.';

function GetTestStream: TStream;
var
  Bytes: TBytes;
begin
  Result := TMemoryStream.Create;
  // fill the stream with 500MB of lorem ipsum
  Bytes := TEncoding.UTF8.GetBytes(LoremIpsum);
  while Result.Size < 500*1024*1024 do
    Result.WriteBuffer(Pointer(Bytes)^,Length(Bytes));
end;

procedure DoTest;
var
  DataStream,ZipStream: TStream;
  Stopwatch: TStopwatch;
  Zip: TZipFile;
begin
  DataStream := GetTestStream;
  try
    ZipStream := TMemoryStream.Create;
    try
      Zip := TZipFile.Create;
      try
        Zip.Open(ZipStream,zmWrite);

        Stopwatch := TStopwatch.StartNew;
        DataStream.Position := 0;
        Zip.Add(DataStream,'foo');
        Writeln(Stopwatch.ElapsedMilliseconds);
      finally
        Zip.Free;
      end;
    finally
      ZipStream.Free;
    end;
  finally
    DataStream.Free;
  end;
end;

begin
  DoTest;
end.

我在XE2和XE7下编译了32位和64位的程序,并配置了默认的发行版配置编译器选项.我的测试机器在Intel Xeon E5530上运行Windows 7 x64.

结果如下:

Compiler  Target  Time (ms)
     XE2   Win32       8586
     XE2   Win64      18908
     XE7   Win32       8583
     XE7   Win64      19304

我使用Explorer shell ZIP功能压缩了同一个文件,我的粗略停止时间为8秒,所以上面的32位时间似乎是合理的.

由于上述代码使用的压缩算法是zlib(Delphi的ZIP代码仅支持存储和缩小),所以我认为Delphi使用的zlib库是此问题的根源.为什么Delphi的zlib库在64位之前这么慢?

解决方法

如前所述,Delphi ZIP压缩代码站在zlib之上. zlib的Delphi实现是官方zlib C源代码的封装. C代码被编译成对象,然后与{$LINK}链接.对于XE7,System.ZLib顶部的注释表示使用了zlib 1.2.8.

在显而易见的假设下,时间在zlib代码中被使用,对于行为最合理的解释是,64位编译对象对性能不佳负责.所使用的编译器都是发出弱码,或者使用编译器选项的选择不佳.

所以,我采取了以下步骤:

>我下载了zlib 1.2.8的源代码,并用Microsoft 64位编译器编译.
>使用VS2010编译器,版本16.00.30319.01.我编译的对象有以下选项:/ O2 / GS-.
>然后我拿了一个System.ZLib.pas的副本,并将其包含在我的项目中,以及新编译的对象.这确保了使用新编译的zlib对象.
我用XE7编译了64位的Delphi程序.

运行时间在同一机器上用于生成问题中的数据为6,912ms.

然后我重新编译并省略了/ O2选项,并再次循环.这次运行时间为20,077ms.所以我假设Embarcadero刚刚忘记了通过优化来编译这些对象.

我向Embarcadero的质量门户网站报道了这个问题:https://quality.embarcadero.com/browse/RSP-9891

如下面的评论所述,依靠编译对象的其他库似乎也有类似的问题.潜在的问题领域包括:

> MidasLib,对象可能不是性能关键.
> Indy,Delphi附带的版本使用相同的zlib对象.
> System.RegularExpressions是PCRE的封装.
> Vcl.Imaging.jpeg,构建在作为编译对象链接的第三方JPEG实现之上.

更新

质量门户问题报告说,这个问题在XE8中已经修复.

相关文章

 从网上看到《Delphi API HOOK完全说明》这篇文章,基本上都...
  从网上看到《Delphi API HOOK完全说明》这篇文章,基本上...
ffmpeg 是一套强大的开源的多媒体库 一般都是用 c/c+&#x...
32位CPU所含有的寄存器有:4个数据寄存器(EAX、EBX、ECX和ED...
1 mov dst, src dst是目的操作数,src是源操作数,指令实现的...