PowerShell Slowness два больших файла CSV - PullRequest
0 голосов
/ 03 апреля 2020

У меня работает этот скрипт, но с 100k + строк в File1 и 200k + в file 2, это займет несколько дней. Я получил где. ({До менее чем за секунду, с обоими CSV-файлами в качестве таблиц данных, но с этим маршрутом я не могу вывести данные так, как я хочу. Этот скрипт выводит данные так, как я хочу, но это занимает 4 секунды на поиск. Что я могу сделать, чтобы ускорить это?

Я думал, что содержит ключ где-то может помочь, но на PRACT_ID есть отношение один ко многим, так что не знаете, как справиться с этим? Thx .

Invoke-Expression "C:\SHC\MSO\DataTable\functionlibrary.ps1" 
[System.Data.DataTable]$Script:MappingTable = New-Object System.Data.DataTable
$File1 = Import-csv "C:\File1.csv" -Delimiter '|' | Sort-Object PRACT_ID
$File2 = Get-Content "C:\File2.csv" | Select-Object -Skip 1 | Sort-Object 
$Script:MappingTable = $File1 | Out-DataTable

$Logs = "C:\Testing1.7.csv" 

[System.Object]$UserOutput = @()

foreach ($name in $File1) {

    [string]$userMatch = $File2.Where( { $_.Split("|")[0] -eq $name.PRACT_ID })

    if ($userMatch) {
        # Process the data

        $UserOutput += New-Object PsObject -property @{
            ID_NUMBER                = $name.ID_NUMBER
            PRACT_ID                 = $name.PRACT_ID
            LAST_NAME                = $name.LAST_NAME
            FIRST_NAME               = $name.FIRST_NAME
            MIDDLE_INITIAL           = $name.MIDDLE_INITIAL
            DEGREE                   = $name.DEGREE
            EMAILADDRESS             = $name.EMAILADDRESS
            PRIMARY_CLINIC_PHONE     = $name.PRIMARY_CLINIC_PHONE
            SPECIALTY_NAME           = $name.SPECIALTY_NAME
            State_License            = $name.State_License
            NPI_Number               = $name.NPI_Number
            'University Affiliation' = $name.'University Affiliation'
            Teaching_Title           = $name.Teaching_Title
            FACILITY                 = $userMatch
        }
    }
}


$UserOutput | Select-Object ID_NUMBER, PRACT_ID, LAST_NAME, FIRST_NAME, MIDDLE_INITIAL, DEGREE, EMAILADDRESS, PRIMARY_CLINIC_PHONE, SPECIALTY_NAME, State_License, NPI_Number, 'University Affiliation', Teaching_Title, FACILITY |
Export-Csv $logs -NoTypeInformation 

Ответы [ 2 ]

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

Существует множество способов увеличить скорость выполнения операций, которые можно разбить на возможности в сценариях и вне сценариев:

Возможности вне сценария:

Поскольку файлы имеют большой размер, сколько памяти у компьютера, на котором вы работаете? И максимизируете ли вы его во время этой операции?

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

Если вы есть два способа решения этой проблемы:

  1. Добавьте больше оборудования для решения проблемы (проще всего с этим справиться)
  2. Напишите свой код для перебора каждого файла небольшими порциями за раз, поэтому Вы не загружаете все это в ОЗУ одновременно. (очень сложно, если вы не знакомы с ним)

In-Script:

  • Не используйте @ () с + = (это очень медленно (особенно на больших наборы данных))

Используйте вместо ArrayList. Вот краткий пример разницы производительности (ArrayList ~ в 40 раз быстрее на 10 000 и в 500 раз быстрее на 100 000 записей, соответственно - эта разница увеличивается с увеличением набора данных, или, другими словами, @() += становится медленнее с увеличением набора данных )):

(Measure-Command {
    $arr = [System.Collections.ArrayList]::new()

    1..100000 | % {
        [void]$arr.Add($_)
    }
}).TotalSeconds

(Measure-Command {
    $arr = @()
    1..100000 | % {
        $arr += $_
    }
}).TotalSeconds

0.8258113
451.5413987
  • Если вам нужно выполнить многократный поиск данных на основе ключей, итерации по данным миллионы раз будут медленными. Импортируйте данные как CSV, а затем структурируйте пару хеш-таблиц со связанной информацией с помощью key -> data и / или key -> data[], и тогда вы сможете выполнять поиск по индексу, вместо того, чтобы повторять массивы миллионы раз ... это будет НАМНОГО быстрее ; при условии, что у вас есть доступная оперативная память для дополнительных объектов ..

РЕДАКТИРОВАТЬ для @RoadRunner:

Мой опыт работы с G C может быть старым ... раньше он был ужасно медленным для больших файлов, но появляется в более новых версиях PowerShell, возможно, были исправлены:

[System.IO.File]::WriteAllLines("$($Env:UserProfile)\Desktop\10MB.txt", ('8' * 10MB))
[System.IO.File]::WriteAllLines("$($Env:UserProfile)\Desktop\50MB.txt", ('8' * 50MB))
[System.IO.File]::WriteAllLines("$($Env:UserProfile)\Desktop\100MB.txt", ('8' * 100MB))
[System.IO.File]::WriteAllLines("$($Env:UserProfile)\Desktop\500MB.txt", ('8' * 500MB))

$10MB  = gi .\10MB.txt
$50MB  = gi .\50MB.txt
$100MB = gi .\100MB.txt
$500MB = gi .\500MB.txt

0..10 | % { 
    $n = [pscustomobject] @{
        'GC_10MB'    = (Measure-Command { Get-Content $10MB }).TotalSeconds
        'RAL_10MB'   = (Measure-Command { [System.IO.File]::ReadAllLines($10MB) }).TotalSeconds
        'GC_50MB'    = (Measure-Command { Get-Content $50MB }).TotalSeconds
        'RAL_50MB'   = (Measure-Command { [System.IO.File]::ReadAllLines($50MB) }).TotalSeconds
        'GC_100MB'   = (Measure-Command { Get-Content $100MB }).TotalSeconds
        'RAL_100MB'  = (Measure-Command { [System.IO.File]::ReadAllLines($100MB) }).TotalSeconds
        'GC_500MB'   = (Measure-Command { Get-Content $500MB }).TotalSeconds
        'RAL_500MB'  = (Measure-Command { [System.IO.File]::ReadAllLines($500MB) }).TotalSeconds
        'Delta_10MB'  = $null
        'Delta_50MB'  = $null
        'Delta_100MB' = $null
        'Delta_500MB' = $null
    }

    $n.Delta_10MB  = "{0:P}" -f ($n.GC_10MB / $n.RAL_10MB)
    $n.Delta_50MB  = "{0:P}" -f ($n.GC_50MB / $n.RAL_50MB)
    $n.Delta_100MB = "{0:P}" -f ($n.GC_100MB / $n.RAL_100MB)
    $n.Delta_500MB = "{0:P}" -f ($n.GC_500MB / $n.RAL_500MB)

    $n
}
2 голосов
/ 03 апреля 2020

Загрузка $File2 в хеш-таблицу со значением $_.Split('|')[0] в качестве ключа - вы также можете полностью пропустить создание объекта и выгружать все в Select-Object:

$File2 = Get-Content "C:\File2.csv" | Select-Object -Skip 1 | Sort-Object 

# load $file2 into hashtable
$userTable = @{}
foreach($userEntry in $File2){
    $userTable[$userEntry.Split('|')[0]] = $userEntry
}

# prepare the existing property names we want to preserve
$propertiesToSelect = 'ID_NUMBER', 'PRACT_ID', 'LAST_NAME', 'FIRST_NAME', 'MIDDLE_INITIAL', 'DEGREE', 'EMAILADDRESS', 'PRIMARY_CLINIC_PHONE', 'SPECIALTY_NAME', 'State_License', 'NPI_Number', 'University Affiliation', 'Teaching_Title'

# read file, filter on existence in $userTable, add the FACILITY calculated property before export
Import-csv "C:\File1.csv" -Delimiter '|' |Where-Object {$userTable.ContainsKey($_.PRACT_ID)} |Select-Object $propertiesToSelect,@{Name='FACILITY';Expression={$userTable[$_.PRACT_ID]}} |Export-Csv $logs -NoTypeInformation
...