Многопоточный скрипт PowerShell для более быстрого извлечения данных из большого файла XML - PullRequest
3 голосов
/ 22 апреля 2020

Следующий скрипт работает, как и ожидалось, для получения желаемого результата, но обработка больших файлов XML (2 ГБ и более) занимает много времени. Вызов экспертов для предложений о том, как сделать это быстрее с помощью многопоточности или с использованием некоторой другой техники в скрипте powershell.

Справочный пост - узнать больше о логи c приведенного ниже скрипта: Parse XML для извлечения данных с группировкой в ​​PowerShell

# Create XML object to load data into
$xml = New-Object -TypeName System.Xml.XmlDocument

# Load in XML file
$xml.Load("test.xml")

# Group XML child nodes by Priority
$groups = $xml.'ABC-FOF-PROCESS'.ChildNodes | Group-Object -Property PRIORITY

# Iterate groups and create PSCustomObject for each grouping
& {
    foreach ($group in $groups)
    {
        [PSCustomObject]@{
            PRIORITY = [int]$group.Name
            KEY = ($group.Group.KEY | Select-Object -Unique).Count
            HITS = $group.Count
        }
    }
} | Sort-Object -Property PRIORITY -Descending | Out-File -FilePath output.txt
# Pipe output here

Вывод:

PRIORITY KEY HITS
-------- --- ----
       1   1    1
      -3   2    2
     -14   2    3

xml:

<ABC-FOF-PROCESS>
<H>
 <PRIORITY>-14</PRIORITY>
 <KEY>F637A146-3437AB82-BA659D4A-17AC7FBF</KEY>
</H>
<H>
 <PRIORITY>-14</PRIORITY>
 <KEY>F637A146-3437AB82-BA659D4A-17AC7FBF</KEY>
</H>
<H>
 <PRIORITY>-3</PRIORITY>
 <KEY>D6306210-CF424F11-8E2D3496-E6CE1CA7</KEY>
</H>
<H>
 <PRIORITY>1</PRIORITY>
 <KEY>D6306210-CF424F11-8E2D3496-E6CE1CA7</KEY>
</H>
<H>
 <PRIORITY>-3</PRIORITY>
 <KEY>4EFR02B4-ADFDAF12-3C123II2-ADAFADFD</KEY>
</H>
<H>
 <PRIORITY>-14</PRIORITY>
 <KEY>5D2702B2-ECE8F1FB-3CEC3229-5FE4C4BC</KEY>
</H>
</ABC-FOF-PROCESS>

Ответы [ 2 ]

2 голосов
/ 22 апреля 2020

Если ваш xml - фиксированный формат, вы можете читать файл построчно и корректировать результаты, как вы go.

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

$hits = @{} # Hashtable containing number of hits per priority
$keys = @{} # Hashtable containing unique keys per priority
switch -Regex -File $env:temp\test.xml
{
    '^\s+<PRIORITY>(?<priority>[-]?\d+)'
    {
        $currentPriority = $matches.Priority
        $hits[$currentPriority] = $hits[$currentPriority]+1
        continue
    }
    '^\s+<KEY>(?<key>[\w-]+)'
    {
        $currentKey = $matches.Key
        if ($keys[$currentPriority] -eq $null) {$keys[$currentPriority] = @{}}
        $keys[$currentPriority][$currentKey] = $null
    }
}

$hits.GetEnumerator() | % {
    [PSCustomObject]@{
        PRIORITY = [int]$_.Key
        KEY = $keys[$_.Key].Count
        HITS = [int]$_.Value
    }
} | Sort PRIORITY -Descending

Протестировано на 500 МБ xml

PRIORITY KEY    HITS
-------- ---    ----
       1   1 1000000
      -3   2 2000000
     -14   2 3000000

$timer

IsRunning Elapsed          ElapsedMilliseconds ElapsedTicks
--------- -------          ------------------- ------------
    False 00:02:25.7186698              145718    413249113
1 голос
/ 22 апреля 2020

Я полагаю, что это один из примеров, где основное внимание уделялось одной команде ( Время выполнения объекта Foreach-объект против Foreach l oop), а не завершению решения.

В целом, я бы порекомендовал вам взглянуть на все ваше решение, а не только на отдельные утверждения, поскольку производительность полного (PowerShell) решения должна быть лучше, чем сумма его частей .

В вашем случае, если вам нужно создать экземпляр сценария и вызвать его с помощью оператора вызова & только потому, что вы хотите использовать оператор Foreach, вы, вероятно, стремитесь к цели:

Для небольшого предоставленного вами файла,
This (используя конвейер с ForEach-Object):

$groups | ForEach-Object {
    [PSCustomObject]@{
        PRIORITY = [int]$_.Name
        KEY = ($_.Group.KEY | Select-Object -Unique).Count
        HITS = $_.Count
    }
} | Sort-Object -Property PRIORITY -Descending # | Out-File -FilePath output.txt

В целом выглядит быстрее, чем это (с помощью оператора ForEach и оператор вызова):

& {
    foreach ($group in $groups)
    {
        [PSCustomObject]@{
            PRIORITY = [int]$group.Name
            KEY = ($group.Group.KEY | Select-Object -Unique).Count
            HITS = $group.Count
        }
    }
} | Sort-Object -Property PRIORITY -Descending | Out-File -FilePath output.txt

В связи с характером командлета Sort-Object (все объекты должны иметь возможность его сортировать), ему потребуется остановить конвейер, чтобы изменить их порядок, для того же причина, мульти Подход с многопоточностью, вероятно, не имеет особого смысла.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...