При написании "Заполнение страницы 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
ОтветитьУдалитьона по скорости интреснее.