Почему библиотеки Delphi zlib и zip настолько медленны до 64 бит?
Во время тестирования реального приложения я столкнулся с удивительной характеристикой производительности, связанной с библиотеками zlib и zip, которые поставляются с Delphi.
My real-world приложение экспортирует файлы .xlsx. Этот формат файла представляет собой набор файлов XML, завернутых в файл контейнера ZIP. Экспортный код .xlsx генерирует файлы XML, а затем передает их в ZIP-архив Delphi. Как только я оптимизировал генерацию файла 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 бит, а также для параметров компилятора конфигурации по умолчанию. Моя тестовая машина работает под управлением Windows 7 x64 на Intel Xeon E5530.
Вот результаты:
Compiler Target Time (ms)
XE2 Win32 8586
XE2 Win64 18908
XE7 Win32 8583
XE7 Win64 19304
Я сжал один и тот же файл, используя функциональность ZIP-оболочки обозревателя, и мое грубое время остановки секундомера составляло 8 секунд, поэтому 32-битные времена выше кажутся разумными.
Поскольку алгоритм сжатия, используемый вышеуказанным кодом, является zlib (индексный код Delphi поддерживает только сохранение и дефляцию), я считаю, что библиотека zlib, используемая Delphi, лежит в основе этой проблемы. Почему библиотека Delphi zlib настолько медленная до 64 бит?
Ответы
Ответ 1
Как уже отмечалось, код сжатия ZIP для Delphi стоит поверх 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.
- Я скомпилировал программу Delphi с XE7 для 64 бит.
Время выполнения на том же компьютере, что и для генерации данных в вопросе, составляло 6,912 мс.
Затем я перекомпилировал и опустил параметр /O2
и снова обвел цикл. На этот раз время работы составляло 20 077 мс. Поэтому я полагаю, что Embarcadero просто забыл скомпилировать эти объекты с оптимизацией.
Я опубликовал эту проблему на портале качества Embarcadero: https://quality.embarcadero.com/browse/RSP-9891
Как упоминалось в комментарии ниже, представляется вполне правдоподобным, что другие библиотеки, которые полагаются на скомпилированные объекты, могут иметь схожие проблемы. Возможные проблемные области:
- MidasLib, объекты, вероятно, не критичны по производительности.
- Indy, версия, поставляемая с Delphi, использует те же объекты zlib, которые я считаю.
- System.RegularExpressions, оболочка вокруг PCRE.
- Vcl.Imaging.jpeg, построенный на основе сторонней реализации JPEG, которая связана как скомпилированные объекты.
Обновление
Проблема с качественным порталом сообщает, что эта проблема была исправлена в XE8.