Выполните итерацию заархивированного текстового файла ascii, найдите все экземпляры {LINE2 1-9999} и замените {LINE2 "номер строки, в которой находится код"}.Перезапись.Быстрее? - PullRequest
0 голосов
/ 18 февраля 2019

Этот код работает.Я просто хочу посмотреть, насколько быстрее кто-то сможет заставить его работать.

Сделайте резервную копию вашего пакетного файла Windows 10 на случай, если что-то пойдет не так.Найдите все экземпляры строки {LINE2 1-9999} и замените на {LINE2 "номер строки, в которой находится код"}.Перезаписать, кодировать как ASCII.

Если _61.bat равен:

TITLE %TIME%   NO "%zmyapps1%\*.*" ARCHIVE ATTRIBUTE   LINE2 1243
TITLE %TIME%   DOC/SET YQJ8   LINE2 1887
SET ztitle=%TIME%: WINFOLD   LINE2 2557
TITLE %TIME%   _*.* IN WINFOLD   LINE2 2597
TITLE %TIME%   %%ZDATE1%% YQJ25   LINE2 3672
TITLE %TIME%   FINISHED. PRESS ANY KEY TO SHUTDOWN ... LINE2 4922

Результаты:

TITLE %TIME%   NO "%zmyapps1%\*.*" ARCHIVE ATTRIBUTE   LINE2 1
TITLE %TIME%   DOC/SET YQJ8   LINE2 2
SET ztitle=%TIME%: WINFOLD   LINE2 3
TITLE %TIME%   _*.* IN WINFOLD   LINE2 4
TITLE %TIME%   %%ZDATE1%% YQJ25   LINE2 5
TITLE %TIME%   FINISHED. PRESS ANY KEY TO SHUTDOWN ... LINE2 6

Код:

Copy-Item $env:windir\_61.bat -d $env:temp\_61.bat
(gc $env:windir\_61.bat) | foreach -Begin {$lc = 1} -Process {
    $_ -replace "LINE2 \d*", "LINE2 $lc";
    $lc += 1
} | Out-File -Encoding Ascii $env:windir\_61.bat

Iожидать, что это займет менее 984 миллисекунд.Это займет 984 миллисекунды.Вы можете придумать что-нибудь, чтобы ускорить это?

1 Ответ

0 голосов
/ 18 февраля 2019

Ключ для повышения производительности в коде PowerShell (если не считать встраивания кода C #, скомпилированного по требованию с помощью Add-Type, что может или не может помочь):

  • избегать использования командлетов и конвейера в целом ,
    • , особенно при вызове блока скрипта ({...}) для каждого входного объекта конвейера, например с ForEach-Object.
  • для избежания конвейера требуется прямое использование типов .NET Framework в качестве альтернативы командлетам .
  • если возможно , используйте switch операторы для массив или построчно файл обработка - switch операторы, как правило, превосходят foreach циклы.

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

В вашем случае следующий код, который объединяет оператор switch с прямым использованием .NПлатформа ET для файлового ввода-вывода , по-видимому, обеспечивает наилучшую производительность - обратите внимание, что входной файл считывается в память в целом как массив строк, и копия этого массива сизмененные строки создаются до того, как они записываются обратно во входной файл:

$file = "$env:temp\_61.bat" # must be a *full* path.
$lc = 0
$updatedLines = & { switch -Regex -File $file {
  '^(.*? LINE2 )\d+(.*)$' { $Matches[1] + ++$lc + $Matches[2] }
  default { ++$lc; $_ } # pass non-matching lines through
} }
[IO.File]::WriteAllLines($file, $updatedLines, [Text.Encoding]::ASCII)

Примечание:

  • Заключение оператора switch в & { ... } неясноОптимизация производительности объяснена в этом ответе .

  • Если сопоставление с регистром чувствительным является достаточным, как показано в примере ввода, вы можете улучшить производительность немного больше, добавив параметр -CaseSensitive к команде switch.

В моих тестах (см. Ниже) это дало более 4-кратность улучшения производительности Windows PowerShell по сравнению с вашей командой.


Вот сравнение производительности с помощью функции Time-Command :

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

  • Команда switch сверху.

  • Немного упрощенная версия вашей собственной команды.

  • PowerShell Core v6.1 + альтернатива, которая использует-replace оператор с массивом строк в качестве LHS и скрипт-блок в качестве выражения замены.

Вместо файла с 6 строками, 6000-файл строки используется.100 прогонов усредняются.Эти параметры легко настроить.

# Sample file content (6 lines)
$fileContent = @'
TITLE %TIME%   NO "%zmyapps1%\*.*" ARCHIVE ATTRIBUTE   LINE2 1243
TITLE %TIME%   DOC/SET YQJ8   LINE2 1887
SET ztitle=%TIME%: WINFOLD   LINE2 2557
TITLE %TIME%   _*.* IN WINFOLD   LINE2 2597
TITLE %TIME%   %%ZDATE1%% YQJ25   LINE2 3672
TITLE %TIME%   FINISHED. PRESS ANY KEY TO SHUTDOWN ... LINE2 4922

'@

# Determine the full path to a sample file.
# NOTE: Using the *full* path is a *must* when calling .NET methods, because
#       the latter generally don't see the same working dir. as PowerShell.
$file = "$PWD/test.bat"

# Create the sample file with the sample content repeated N times.
$repeatCount = 1000 # -> 6,000 lines
[IO.File]::WriteAllText($file, $fileContent * $repeatCount)

# Warm up the file cache and count the lines.
$lineCount = [IO.File]::ReadAllLines($file).Count

# Define the commands to compare as an array of scriptblocks.
$commands =
  { # switch -Regex -File + [IO.File]::Read/WriteAllLines()
    $i = 0
    $updatedLines = & { switch -Regex -File $file {
      '^(.*? LINE2 )\d+(.*)$' { $Matches[1] + ++$i + $Matches[2] }
      default { ++$lc; $_ }
    } }
   [IO.File]::WriteAllLines($file, $updatedLines, [text.encoding]::ASCII)
  },
  { # Get-Content + -replace + Set-Content
    (Get-Content $file) | ForEach-Object -Begin { $i = 1 } -Process {
      $_ -replace "LINE2 \d*", "LINE2 $i"
      ++$i
    } | Set-Content -Encoding Ascii $file
  }

# In PS Core v6.1+, also test -replace with a scriptblock operand.
if ($PSVersionTable.PSVersion.Major -ge 6 -and $PSVersionTable.PSVersion.Minor -ge 1) {
  $commands +=
    { # -replace with scriptblock + [IO.File]::Read/WriteAllLines()
      $i = 0
      [IO.File]::WriteAllLines($file,
        ([IO.File]::ReadAllLines($file) -replace '(?<= LINE2 )\d+', { (++$i) }),
        [text.encoding]::ASCII
      )
    }
} else {
  Write-Warning "Skipping -replace-with-scriptblock command, because it isn't supported in this PS version."
}

# How many runs to average.
$runs = 100

Write-Verbose -vb "Averaging $runs runs with a $lineCount-line file of size $('{0:N2} MB' -f ((Get-Item $file).Length / 1mb))..."

Time-Command -Count $runs -ScriptBlock $commands

Вот примеры результатов с моего компьютера с Windows 10 (абсолютные значения времени не важны, но, надеюсь, относительная производительность, показанная в столбце Factor, несколько репрезентативна);Используемая версия PowerShell Core : v6.2.0-preview.4

# Windows 10, Windows PowerShell v5.1

WARNING: Skipping -replace-with-scriptblock command, because it isn't supported in this PS version.
VERBOSE: Averaging 100 runs with a 6000-line file of size 0.29 MB...

Factor Secs (100-run avg.) Command
------ ------------------- -------
1.00   0.108               # switch -Regex -File + [IO.File]::Read/WriteAllLines()...
4.22   0.455               # Get-Content + -replace + Set-Content...


# Windows 10, PowerShell Core v6.2.0-preview 4

VERBOSE: Averaging 100 runs with a 6000-line file of size 0.29 MB...

Factor Secs (100-run avg.) Command
------ ------------------- -------
1.00   0.101               # switch -Regex -File + [IO.File]::Read/WriteAllLines()…
1.67   0.169               # -replace with scriptblock + [IO.File]::Read/WriteAllLines()…
4.98   0.503               # Get-Content + -replace + Set-Content…

...