Почему запись значений ячеек Excel выполняется быстро в VBScript, но медленно в PowerShell?

Почему запись значений ячеек в Excel выполняется намного быстрее в VBScript, чем в PowerShell? Разве PowerShell не является чем-то новым, а VBScript — устаревшим языком сценариев MS?

Пример VBScript (сохранить в filename.vbs) Это выполняется за доли секунды.

Set objExcel = CreateObject("Excel.Application")
objExcel.Visible = false
Set objWorkbook = objExcel.Workbooks.Add()

' Edit: increased number of writes to 500 to make speed difference more noticeable
For row = 1 To 500
     'Edit: using .cells(row,1) instead of .cells(50,1) - this was a mistake
     objWorkbook.workSheets(1).cells(row,1).value = "test"
Next

objWorkbook.SaveAs(CreateObject("Scripting.FileSystemObject").GetParentFolderName(WScript.ScriptFullName) & "\test.xlsx")
objExcel.Quit
msgbox "Done."

Пример PowerShell (сохранить в файле имя.ps1) Запуск занимает несколько секунд (проблема при тысячах записей)

#need this to work around bug if you use a non-US locale: http://support.microsoft.com/default.aspx?scid=kb;en-us;320369
[System.Threading.Thread]::CurrentThread.CurrentCulture = "en-US" 

$excel = New-Object -ComObject Excel.Application
$excel.Visible = $False
$xls_workbook = $excel.Workbooks.Add()

# Edit: using foreach instead of for
# Edit: increased number of writes to 500 to make speed difference more noticeable
foreach ($row in 1..500) {
    # Edit: Commented out print-line, slows down the script
    #"Row " + $row
    # This is very slow! - http://forums.redmondmag.com/forums/forum_posts.asp?tid=4037&pn=7
    $xls_workbook.sheets.item(1).cells.item($row,1) = "test"
}

$xls_workbook.SaveAs($MyInvocation.MyCommand.Definition.Replace($MyInvocation.MyCommand.Name, "") + "test.xlsx")
$excel.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel)

Я хочу использовать это для тысяч записей. Если нет быстрого способа сделать это, PowerShell не вариант. Есть ли лучшие альтернативы?


person Wouter    schedule 16.10.2012    source источник
comment
больше мощности обычно связано с большими потребностями в ресурсах ком/вопросы/9343413/   -  person Slai    schedule 08.08.2017


Ответы (5)


Вы можете ускорить процесс, не перебирая отдельные ячейки:

$excel = New-Object -ComObject Excel.Application
$excel.Visible = $True
$xls_workbook = $excel.Workbooks.Add()

$range = $xls_workbook.sheets.item(1).Range("A1:A100")
$range.Value2 = "test"

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

Как очень быстро получить данные в таблицу Excel с помощью PowerShell

person Anonimista    schedule 16.10.2012
comment
Хорошо, я предполагаю, что ссылка на попытку массива сделала это решением, но мне бы очень хотелось знать, работает ли PS с ним быстрее по сравнению с решением массива в VB. @Wouter - было бы неплохо, если бы вы могли опубликовать новые результаты сравнения. - person Jook; 17.10.2012
comment
Хотя на самом деле это не ответ на вопрос, я отмечу его как принятый ответ. Он выполняет то, что я хотел сделать; быстрая запись в Excel из PowerShell. Я собираюсь предположить, что писать ячейку за ячейкой каким-то образом не получится в PowerShell, в то время как это работает в VBScript. Это похоже на шаг назад в удобстве использования PowerShell по сравнению с VBScript. Если у кого-то есть объяснение разницы в скорости, не стесняйтесь, дайте мне знать! - person Wouter; 17.10.2012
comment
@Jook - я тестировал метод Range до 1 000 000 операций записи в PS и VBS, в обоих случаях это занимает около 3 секунд, поэтому разница во времени, если она есть, незначительна. Кроме того, это показывает, что метод Range также является лучшим методом для использования в VBScript, поскольку с методом Cell это займет целую вечность. - person Wouter; 17.10.2012

некоторые вещи не складываются здесь:

ваш VBScript записывает в ОДНУ ячейку снова и снова, в то время как ваш код PowerShell записывает в 100 ячеек

objWorkbook.workSheets(1).cells(50,1).value = "test"

$xls_workbook.sheets.item(1).cells.item($row,1) = "test"

вы выполняете "Row " + $row в PowerShell - это также может компенсировать сравнение.

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

person Jook    schedule 16.10.2012
comment
Я не запускал версию VBScript, но со 100 записями вывод "Row " + $row добавляет примерно полсекунды (общее время выполнения для меня было менее 5 секунд). - person alroc; 16.10.2012
comment
вы должны правильно запустить версию VBScript - заполнение отдельных ячеек также снижает производительность в VB - на моей машине это заняло около секунды - без части сохранения (i7@3,4GHz 8GB Ram). - person Jook; 16.10.2012
comment
Запустив исправленную VBS (используя row вместо 50), я получил 1,391 секунды — в 3 раза быстрее, чем версия PowerShell. - person alroc; 16.10.2012
comment
Эй, Джук, извини, (50, 1) было ошибкой, которая действительно должна была быть ($ row, 1). Я также увеличил количество строк до 500, чтобы сделать разницу в скорости более очевидной. В моей системе с текущим скриптом VBS завершается примерно за секунду, а PS за 20 секунд. С 10 000 строк vbs завершается за 9 секунд. Я не хочу знать, сколько времени это займет в PS... - person Wouter; 17.10.2012
comment
вот и некоторые результаты ;) извините, но я мало что знаю о PS, чтобы помочь вам в дальнейшем; +1 за ваш вопрос, потому что вы меня очень заинтересовали решением этой проблемы. - person Jook; 17.10.2012
comment
6 м 15 с для запуска в PowerShell с 10 000 итераций. Quad Core i7 с гиперпоточностью, я увидел загрузку ЦП около 9%, что было бы правильно — Excel работал так быстро, как только мог. - person alroc; 17.10.2012

Вы можете сэкономить немного времени в версии PowerShell, исключив тест цикла for и используя тест foreach.

for ($row = 1; $row -le 100; $row++)

идет к:

foreach ($row in 1..100)

Делая это, вы исключаете сравнение и приращение.

Но кроме этого, мои наблюдения совпадают с вашими (см. мои комментарии к ответу Джука).

person alroc    schedule 16.10.2012
comment
Спасибо за совет. Тем не менее, это, кажется, лишь незначительное улучшение скорости, если оно вообще есть. - person Wouter; 17.10.2012
comment
Снова и снова было доказано, что for работает быстрее, чем foreach. Я уверен, что в powershell это ничем не отличается ... так что это будет незначительное УМЕНЬШЕНИЕ скорости, хех ... - person user2455808; 26.07.2019

Однако вы по-прежнему взаимодействуете с Excel через COM. Это добавляет некоторые накладные расходы из-за обработки COMInterop.

person Skatterbrainz    schedule 22.11.2012
comment
Разве VBScript не использует COM-объект? - person Wouter; 22.11.2012

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

Прежде чем кто-нибудь бросит меня в грязь и скажет, что я просто не использую PowerShell, я должен упомянуть, что у меня за плечами долгая история написания сценариев оболочки UNIX. PowerShell, очевидно, похож, но, на мой взгляд, реализован не так хорошо.

Я знаю, что реальность диктует, что рано или поздно я перейду к использованию PowerShell — я просто надеюсь, что в будущем он превратится в более «стандартную» замену.

person Thumper    schedule 20.04.2013
comment
Справедливо. Я сделал небольшой сценарий оболочки UNIX еще в конце 1980-х годов, поэтому я могу оценить вашу точку зрения. - person Skatterbrainz; 25.04.2013