При написании "Заполнение страницы MS Excel одной командой" я вспомнил про библиотеки работающие с файлом MS Excel "напрямую". Посмотрим, насколько чтение информации из MS Excel "напрямую" быстрее, чем при использовании OLE.
Создадим файл с 1 000 000 строк по 5 столбцов и прочитаем его различными способами.
Вариант 1. Чтение листа MS Excel через OLE-сервер по ячейкам
procedure ReadCom;
var
ovEA: OleVariant;
iRow, iCol, iRowCount, iColCount: Integer;
dStart: TDateTime;
iSum: Int64;
begin
iSum := 0;
dStart := Now;
Write('1. Excel.Application & Cells: ');
ovEA := CreateOleObject('Excel.Application');
try
ovEA.DisplayAlerts := False; // отключаем сообщения
ovEA.EnableEvents := False; // отключаем обработку событий
ovEA.ScreenUpdating := False; // отключаем перерисовку объектов на экране
ovEA.DisplayStatusBar := False; // отключаем вывод в строку статуса
ovEA.Workbooks.Open(sFileName);
iRowCount := ovEA.ActiveSheet.UsedRange.Rows.Count;
iColCount := ovEA.ActiveSheet.UsedRange.Columns.Count;
for iRow := 1 to iRowCount do
for iCol := 1 to iColCount do
iSum := iSum + ovEA.Cells[iRow, iCol].Value;
finally
ovEA.Quit;
ovEA := Unassigned;
end;
WriteLn(FormatDateTime('nn:ss.zzz', Now - dStart), ' Sum = ', iSum);
end;
РезультатыВариант 2. Чтение листа MS Excel через OLE-сервер диапазоном ячеек32-бит: 58:23.060 - Час! Час, Карл!!! 64-бит: 47:01.807 - это уже лучше
procedure ReadComAsArray;
var
ovEA: OleVariant;
iRow, iCol, iRowCount, iColCount: Integer;
dStart: TDateTime;
iSum: Int64;
v: Variant;
begin
iSum := 0;
dStart := Now;
Write('2. Excel.Application & VarArray: ');
ovEA := CreateOleObject('Excel.Application');
try
ovEA.DisplayAlerts := False; // отключаем сообщения
ovEA.EnableEvents := False; // отключаем обработку событий
ovEA.ScreenUpdating := False; // отключаем перерисовку объектов на экране
ovEA.DisplayStatusBar := False; // отключаем вывод в строку статуса
ovEA.Workbooks.Open(sFileName);
iRowCount := ovEA.ActiveSheet.UsedRange.Rows.Count;
iColCount := ovEA.ActiveSheet.UsedRange.Columns.Count;
v := VarArrayCreate([1, iRowCount, 1, iColCount], varInteger);
try
v := ovEA.Range['A1', Chr($40 + iColCount) + IntToStr(iRowCount)].Value;
for iRow := 1 to iRowCount do
for iCol := 1 to iColCount do
iSum := iSum + v[iRow, iCol];
finally
VarClear(v);
end;
finally
ovEA.Quit;
ovEA := Unassigned;
end;
WriteLn(FormatDateTime('nn:ss.zzz', Now - dStart), ' Sum = ', iSum);
end;
РезультатыЧтение диапазона ячеек и присваивание его динамическому вариантному массиву дает приемлемое время...32-бит: 00:06.326 64-бит: 00:06.241
Вариант 3. Чтение листа MS Excel используя TMS FlexCel
procedure ReadFlexCel;
var
xls: TXlsFile;
iRow, iCol, iRowCount, iColCount: Integer;
dStart: TDateTime;
eSum: Extended;
begin
eSum := 0;
dStart := Now;
Write('3. FlexCel: ');
xls := TXlsFile.Create(sFileName);
try
xls.IgnoreFormulaText := True;
iRowCount := xls.RowCount;
iColCount := xls.ColCount;
for iRow := 1 to iRowCount do
for iCol := 1 to iColCount do
eSum := eSum + xls.GetCellValue(iRow, iCol).AsNumber;
finally
xls.Free;
end;
WriteLn(FormatDateTime('nn:ss.zzz', Now - dStart), ' Sum = ', Trunc(eSum));
end;
РезультатыВот это поворот! TMS FlexCel медленее, чем OLE-сервер MS Excel! Такого я не ожидал от библиотеки, читающей файл MS Excel "напрямую" :-( При этом 64-битная версия читает медленнее 32-х битной (это я замечал раньше на своих утилитах). Вот, что написано по этому поводу в документации по FlexCel: "Going 64-bits might not only not improve performance, it might decrease it. In our tests in a 4Gb machine, 64-bit version is consistently slower than the 32-bit one (both versions running in a 64-bit Operating System).". Но я запускал тест на компьютерах, где оперативной памяти не 4ГБ, а 32ГВ! т.е. разработчики, что-то не договариваю и дело не в объеме оперативной памяти. Возможно, дело в вызове ColCount, который пробегает по строкам и находит самую длинную строку? В документации написано "TExcelFile.ColCount is a very slow method in FlexCel, and it is mostly useless". Заменим вызов "iColCount := xls.ColCount" на "iColCount := 5;":32-бит: 00:07.095 64-бит: 00:07.749
Лучше, но не на много...32-бит: 00:06.931 64-бит: 00:07.712
Вариант 4. Чтение листа MS Excel используя TMS FlexCel VirtualMode
Но у FlexCel есть еще козырь в рукаве! Это - "Virtual mode". При его использовании, программа не загружает файл целиком в память, а читает ячейки и после прочтения "забывает". Тем самым расход памяти падает, а быстродействие возрастает. В этом режиме не работают многие функции FlexCel, но его можно использовать для программ, которым нужно только прочитать информацию из Excel, т.е. как в нашем случае.
type
TVirtualCellReader = class
private
FSum: Extended;
procedure OnCellRead(const Sender: TObject;
const e: TVirtualCellReadEventArgs);
function GetSum: Int64;
public
constructor Create;
property Sum: Int64 read GetSum;
end;
constructor TVirtualCellReader.Create;
begin
inherited Create;
FSum := 0;
end;
function TVirtualCellReader.GetSum: Int64;
begin
Result := Trunc(FSum);
end;
procedure TVirtualCellReader.OnCellRead(const Sender: TObject;
const e: TVirtualCellReadEventArgs);
begin
FSum := FSum + e.Cell.Value.AsNumber;
end;
procedure ReadFlexCelVirtual;
var
xls: TXlsFile;
CellReader: TVirtualCellReader;
dStart: TDateTime;
iSum: Int64;
begin
dStart := Now;
Write('4. FlexCel VirtualMode: ');
xls := TXlsFile.Create(True);
try
xls.IgnoreFormulaText := True;
CellReader := TVirtualCellReader.Create;
try
xls.VirtualMode := True;
xls.VirtualCellRead := CellReader.OnCellRead;
xls.Open(sFileName);
iSum := CellReader.Sum;
finally
CellReader.Free;
end;
finally
xls.Free;
end;
WriteLn(FormatDateTime('nn:ss.zzz', Now - dStart), ' Sum = ', iSum);
end;
РезультатыНо и эти результаты далеки от чтения диапазона ячеек листа MS Excel через OLE-сервер. Я разочарован в FlexCel :-(32-бит: 00:06.699 64-бит: 00:07.289
Вариант 5. Чтение листа MS Excel используя XLSReadWrite
procedure ReadXlsRW;
var
XLS: TXLSReadWriteII5;
iRow, iCol, iRowCount, iColCount: Integer;
dStart: TDateTime;
eSum: Extended;
begin
eSum := 0;
dStart := Now;
Write('5. XLSReadWrite: ');
xls := TXLSReadWriteII5.Create(nil);
try
xls.FileName := sFileName;
xls.Read;
iRowCount := xls[0].LastRow;
iColCount := xls[0].LastCol;
for iRow := 0 to iRowCount do
for iCol := 0 to iColCount do
eSum := eSum + XLS[0].AsFloat[iCol, iRow];
finally
xls.Free;
end;
WriteLn(FormatDateTime('nn:ss.zzz', Now - dStart), ' Sum = ', Trunc(eSum));
end;
РезультатыУ нас новый лидер! И это библиотека, от которой я отказался лет 7-9 тому назад... и заменил ее на более производительную и функциональную FlexCel...32-бит: 00:05.259 64-бит: 00:05.301
Вариант 6. Чтение листа MS Excel используя XLSReadWrite DirectRead
За годы моего пользования FlexCel, в XLSReadWrite появился "DirectRead mode", который, по описанию в документации, является аналогом "Virtual mode" из FlexCel.
type
TXLSReadCell = class
private
FSum: Extended;
procedure OnCellRead(ACell: TXLSEventCell);
function GetSum: Int64;
public
constructor Create;
property Sum: Int64 read GetSum;
end;
constructor TXLSReadCell.Create;
begin
inherited Create;
FSum := 0;
end;
function TXLSReadCell.GetSum: Int64;
begin
Result := Trunc(FSum);
end;
procedure TXLSReadCell.OnCellRead(ACell: TXLSEventCell);
begin
FSum := FSum + ACell.AsFloat;
end;
procedure ReadXlsRWdr;
var
XLS: TXLSReadWriteII5;
CellReader: TXLSReadCell;
dStart: TDateTime;
iSum: Int64;
begin
dStart := Now;
Write('6. XLSReadWrite DirectRead: ');
xls := TXLSReadWriteII5.Create(nil);
try
xls.FileName := sFileName;
xls.DirectRead := True;
CellReader := TXLSReadCell.Create;
try
xls.OnReadCell := CellReader.OnCellRead;
xls.Read;
iSum := CellReader.Sum;
finally
CellReader.Free;
end;
finally
xls.Free;
end;
WriteLn(FormatDateTime('nn:ss.zzz', Now - dStart), ' Sum = ', iSum);
end;
РезультатыПрирост почти на секунду! "DirectRead mode" в XLSReadWrite, в отличии "Virtual mode" в FlexCel, дает реальный прирост производительности.32-бит: 00:04.349 64-бит: 00:04.467
Итак, подведем итоги:
1. Axolot XLSReadWrite "DirectRead mode" - 4.349 сек
2. Axolot XLSReadWrite - 5.259 сек
3. MS Excel через OLE-сервер диапазоном ячеек - 6.241 сек
4. TMS FlexCel "Virtual mode" - 6.699 сек
5. TMS FlexCel - 7.095 сек
Таким образом, уверенную победу в чтении информации с листа MS Excel, опережая конкурентов в полтора и более раз, одержала библиотека XLSReadWrite от Axolot Data. Что еще сказать? Умеют шведы делать качественные продукты: Volvo, Ericsson, MySQL, IKEA... и XLSReadWrite...
Не знаю можно ли адаптировать для Delphi, но для FreePascal есть библиотека http://wiki.lazarus.freepascal.org/FPSpreadsheet
ОтветитьУдалитьона по скорости интреснее.