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

Dav*_*nan 17 delphi delphi-xe2 delphi-xe7

在对现实世界的应用程序进行基准测试时,我遇到了与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.
Run Code Online (Sandbox Code Playgroud)

我在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的邮政编码仅支持存储和收缩),我相信Delphi使用的zlib库是这个问题的根源.为什么Delphi的zlib库在64位下如此之慢?

Dav*_*nan 21

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

在zlib代码中花费时间的明显假设下,对行为最合理的解释是64位编译对象导致性能不佳.要么使用的编译器发出弱代码,要么使用了很差的编译器选项.

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

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

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

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

我已将此问题报告给Embarcadero的质量门户网站:https://quality.embarcadero.com/browse/RSP-9891

正如下面的评论中所提到的,依赖于编译对象的其他库可能会遇到类似的问题.潜在的问题领域包括:

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

更新

质量门户问题报告此问题已在XE8中得到修复.

  • @DalijaPrasnikar我现在正在这样做.鸡肉和鸡蛋.我想在答案中参考QP,并参考QP中的SO帖子!我在这里发布问答的原因是希望其他可能找不到QP报告的开发人员受益. (2认同)
  • 实际上在`.../Studio/15.0/lib/win32/debug`和`.../win32/release`中有zlib` .obj`文件的单独版本,后者更小,看似用优化编译( CodeGear C++ 6.90编译器).然而,在`.../win64/debug`中,没有`.obj`文件,在`.../win64/release`中``.jj`文件似乎是在调试模式下编译的(MS Optimizing compiler - 关键字`debug`,在编译文件中偏移量为"0x3D"的明文中.这表明其他图书馆也可能受到影响. (2认同)
  • 只是想报告这个问题已经解决了Delphi 10 Seattle(实际上QP表示他们已经在XE 8中解决了这个问题).刚刚使用发布版本进行测试:32位获得3874而64位获得3308,因此64位比32位快一点. (2认同)