Excel Interop - эффективность и производительность - PullRequest
62 голосов
/ 10 декабря 2008

Мне было интересно, что я мог бы сделать, чтобы улучшить производительность автоматизации Excel, поскольку это может быть довольно медленным, если у вас много работы на рабочем листе ...

Вот несколько я нашел себя:

  • ExcelApp.ScreenUpdating = false - отключить перерисовку экрана

  • ExcelApp.Calculation = Excel.XlCalculation.xlCalculationManual - отключение механизма вычислений, чтобы Excel не пересчитывал автоматически при изменении значения ячейки (включите его снова после того, как закончите)

  • Уменьшите количество звонков до Worksheet.Cells.Item(row, col) и Worksheet.Range - мне пришлось опросить сотни ячеек, чтобы найти нужную ячейку. Реализация некоторого кэширования местоположений ячеек сократила время выполнения с ~ 40 до ~ 5 секунд.

Какие вызовы взаимодействия сильно сказываются на производительности и их следует избегать? Что еще вы можете сделать, чтобы избежать ненужной обработки?

Ответы [ 7 ]

47 голосов
/ 19 февраля 2010

При использовании C # или VB.Net для получения или установки диапазона определите общий размер диапазона, а затем получите один большой массив двумерных объектов ...

//get values
object[,] objectArray = shtName.get_Range("A1:Z100").Value2;
iFace = Convert.ToInt32(objectArray[1,1]);

//set values
object[,] objectArray = new object[3,1] {{"A"}{"B"}{"C"}};
rngName.Value2 = objectArray;

Обратите внимание, что важно знать, какой тип данных Excel хранит (текст или числа), поскольку он не будет делать это автоматически для вас при преобразовании типа обратно из массива объектов. При необходимости добавьте тесты для проверки данных, если заранее не уверены в типе данных.

12 голосов
/ 06 октября 2011

Это для тех, кто интересуется, как лучше всего заполнить таблицу Excel из набора результатов db. Это никоим образом не означает, что это полный список, но в нем есть несколько вариантов.

Некоторые показатели производительности при попытке заполнить таблицу Excel 155 столбцами и 4200 записями на старом блоке Pentium 4 3GHz, включая время извлечения данных, которое никогда не превышало 10 секунд в порядке медленной скорости, выглядит следующим образом ...

  1. Одна ячейка за раз - Чуть меньше 11 минут

  2. Заполнение набора данных путем преобразования в html + Сохранение html на диск + Загрузка html в excel и сохранение рабочего листа в формате xls / xlsx - 5 минут

  3. Один столбец за раз - 4 минуты

  4. Использование устаревшей процедуры sp_makewebtask в SQL 2005 для создания файла HTML - 9 секунд + с последующей загрузкой файла html в excel и сохранением в формате XLS / XLSX - около 2 минут.

  5. Преобразование набора данных .Net в ADO RecordSet и использование функции WorkSheet.Range []. CopyFromRecordset для заполнения Excel - 45 секунд!

В итоге я воспользовался вариантом 5. Надеюсь, это поможет.

5 голосов
/ 15 декабря 2008

Если вы опрашиваете значения многих ячеек, вы можете получить все значения ячеек в диапазоне, сохраненном в массиве вариантов, одним махом:

Dim CellVals() as Variant
CellVals = Range("A1:B1000").Value

Здесь есть компромисс в отношении размера диапазона, для которого вы получаете значения. Я предполагаю, что если вам нужна тысяча или более значений ячеек, это, вероятно, быстрее, чем просто перебирать различные ячейки и опрашивать значения.

5 голосов
/ 10 декабря 2008

По возможности используйте встроенные функции превосходства, например: вместо поиска целого столбца для заданной строки, используйте команду find, доступную в графическом интерфейсе по Ctrl-F:

Set Found = Cells.Find(What:=SearchString, LookIn:=xlValues, _
    SearchOrder:=xlByRows, SearchDirection:=xlNext, _
    MatchCase:=False, SearchFormat:=False)

If Not Found Is Nothing Then
    Found.Activate
    (...)
EndIf

Если вы хотите отсортировать некоторые списки, используйте команду excel sort, не делайте этого вручную в VBA:

Selection.Sort Key1:=Range("A1"), Order1:=xlAscending, Header:=xlGuess, _
    OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom, _
    DataOption1:=xlSortNormal
2 голосов
/ 10 декабря 2008

Производительность также во многом зависит от того, как вы автоматизируете Excel. VBA быстрее, чем автоматизация COM, быстрее, чем автоматизация .NET. И обычно раннее (время компиляции) связывание происходит быстрее, чем позднее.

Если у вас есть серьезные проблемы с производительностью, вы можете подумать о переносе критических частей кода в модуль VBA и вызвать этот код из кода автоматизации COM / .NET.

Если вы используете .NET, вам также следует использовать оптимизированные основные сборки взаимодействия, доступные от Microsoft, и не использовать пользовательские сборки взаимодействия.

1 голос
/ 08 февраля 2015

Как говорит Anonymous Type: чтение / запись блоков большого диапазона очень важны для производительности.

В случаях, когда издержки COM-Interop все еще слишком велики, вы можете переключиться на использование интерфейса XLL, который является самым быстрым интерфейсом Excel.

Хотя интерфейс XLL в первую очередь предназначен для пользователей C ++, и XL DNA, и Addin Express предоставляют возможность моста .NET в XLL, что значительно быстрее, чем COM-Interop.

0 голосов
/ 17 июля 2015

Еще одна важная вещь, которую вы можете сделать в VBA, - это использовать Option Explicit и по возможности избегать вариантов. В VBA нельзя избежать на 100% вариантов, но они заставляют интерпретатора выполнять больше работы во время выполнения и тратить память.

Эта статья показалась мне очень полезной, когда я начинал с VBA в Excel.
http://www.ozgrid.com/VBA/SpeedingUpVBACode.htm

А эта книга

http://www.amazon.com/VB-VBA-Nutshell-Language-OReilly/dp/1565923588

аналогично

 app.ScreenUpdates = false //and
 app.Calculation = xlCalculationManual

Вы также можете установить

 app.EnableEvents = false //Prevent Excel events
 app.Interactive = false  //Prevent user clicks and keystrokes

хотя они, кажется, не имеют такого большого значения, как первые два.

Аналогично установке значений Range для массивов, если вы работаете с данными, которые в большинстве случаев представляют собой таблицы с одинаковой формулой в каждой строке столбца, вы можете использовать формулу R1C1 для своей формулы и установить весь столбец, равный формуле Строка, чтобы установить все это в одном вызове.

app.ReferenceStyle = xlR1C1
app.ActiveSheet.Columns(2) = "=SUBSTITUTE(C[-1],"foo","bar")"

Кроме того, создание надстроек XLL с использованием ExcelDNA & .NET (или сложного способа в C) также является единственным способом заставить UDF работать в нескольких потоках. (См. Свойство IsThreadSafe атрибута ExcelFunction в ДНК ДНК.)

Прежде чем полностью перейти на Excel ДНК, я также экспериментировал с созданием видимых COM-библиотек в .NET для использования в проектах VBA. Обработка тяжелого текста в этом случае немного быстрее, чем в VBA, так как вместо коллекции VBA используются обернутые классы .NET List, но Excel ДНК лучше.

...