为什么 Delphi zlib 和 zip 库在 64 位下这么慢?
Why are the Delphi zlib and zip libraries so slow under 64 bit?
在对实际应用程序进行基准测试时,我发现了与 Delphi 附带的 zlib 和 zip 库相关的令人惊讶的性能特征。
我的实际应用程序导出 .xlsx 文件。此文件格式是 XML 个文件的集合,这些文件封装在一个 ZIP 容器文件中。 .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。
结果如下:
编译器目标时间(毫秒)
XE2 Win32 8586
XE2 Win64 18908
XE7 Win32 8583
XE7 Win64 19304
我使用 Explorer shell ZIP 功能压缩了同一个文件,我的粗秒表计时是 8 秒,所以上面的 32 位时间似乎是合理的。
由于上述代码使用的压缩算法是zlib(Delphi的邮编只支持store和deflate),我认为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 位编译器 cl 进行了编译。
- 使用 VS2010 编译器,版本 16.00.30319.01。我使用以下选项编译对象:
/O2 /GS-
。
- 然后我复制了
System.ZLib.pas
并将其与新编译的对象一起包含在我的项目中。这确保使用新编译的 zlib 对象。
- 我用 XE7 为 64 位编译了 Delphi 程序。
在用于生成问题数据的同一台机器上,运行 时间为 6,912 毫秒。
然后我重新编译并省略了 /O2
选项并再次循环。这次 运行 时间是 20,077 毫秒。所以我假设 Embarcadero 只是忘记了用优化来编译这些对象。
我已将此问题报告给 Embarcadero 的质量门户:https://quality.embarcadero.com/browse/RSP-9891
正如下面评论中提到的,其他依赖编译对象的库可能也有类似的问题,这似乎很合理。潜在的问题领域包括:
- MidasLib,对象可能不是性能关键。
- Indy,Delphi 附带的版本使用我认为相同的 zlib 对象。
- System.RegularExpressions,PCRE 的封装。
- Vcl.Imaging.jpeg,建立在作为编译对象链接的第 3 方 JPEG 实现之上。
更新
Quality Portal 问题报告此问题已在 XE8 中修复。
在对实际应用程序进行基准测试时,我发现了与 Delphi 附带的 zlib 和 zip 库相关的令人惊讶的性能特征。
我的实际应用程序导出 .xlsx 文件。此文件格式是 XML 个文件的集合,这些文件封装在一个 ZIP 容器文件中。 .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。
结果如下:
编译器目标时间(毫秒) XE2 Win32 8586 XE2 Win64 18908 XE7 Win32 8583 XE7 Win64 19304
我使用 Explorer shell ZIP 功能压缩了同一个文件,我的粗秒表计时是 8 秒,所以上面的 32 位时间似乎是合理的。
由于上述代码使用的压缩算法是zlib(Delphi的邮编只支持store和deflate),我认为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 位编译器 cl 进行了编译。
- 使用 VS2010 编译器,版本 16.00.30319.01。我使用以下选项编译对象:
/O2 /GS-
。 - 然后我复制了
System.ZLib.pas
并将其与新编译的对象一起包含在我的项目中。这确保使用新编译的 zlib 对象。 - 我用 XE7 为 64 位编译了 Delphi 程序。
在用于生成问题数据的同一台机器上,运行 时间为 6,912 毫秒。
然后我重新编译并省略了 /O2
选项并再次循环。这次 运行 时间是 20,077 毫秒。所以我假设 Embarcadero 只是忘记了用优化来编译这些对象。
我已将此问题报告给 Embarcadero 的质量门户:https://quality.embarcadero.com/browse/RSP-9891
正如下面评论中提到的,其他依赖编译对象的库可能也有类似的问题,这似乎很合理。潜在的问题领域包括:
- MidasLib,对象可能不是性能关键。
- Indy,Delphi 附带的版本使用我认为相同的 zlib 对象。
- System.RegularExpressions,PCRE 的封装。
- Vcl.Imaging.jpeg,建立在作为编译对象链接的第 3 方 JPEG 实现之上。
更新
Quality Portal 问题报告此问题已在 XE8 中修复。