我正在使用PowerShell脚本将ping日志转换为图表数据。
脚本工作正常,但由于数组操作导致运行速度非常慢。
如果脚本在10k行文件上执行,则大约需要7秒。 如果删除了数组操作,则完成所需的时间不到一秒。
我正在寻找替代解决方案,以便在不使用临时数组的情况下将数据返回给调用函数。
输入日志示例:
02.01.2017-14:53:54> Reply from 8.8.8.8: bytes=32 time=184ms TTL=57
02.01.2017-14:53:54> Reply from 8.8.8.8: bytes=32 time=18ms TTL=57
02.01.2017-14:53:59> Request timed out.
02.01.2017-14:54:01> Reply from 192.168.2.186: Destination host unreachable.
02.01.2017-14:54:05> Request timed out.
02.01.2017-14:54:07> Reply from 192.168.2.186: Destination host unreachable.
剧本:
function Convert-V4PingLog2ChartData
{
param($V4PingLogFile, $AvarageRespondTime, $ChartCounter)
$ConvertedData=""
$var=Get-Content $V4PingLogFile
$varArray=$var.split("`n")
$varArray=$varArray | Select-Object -Skip 2
$CommandExecuteTime=Measure-Command{
$pattern = "^([0-9]{2})\.([0-9]{2})\.([0-9]{4})-([0-9]{2}):([0-9]{2}):([0-9]{2})> Reply from [0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}: bytes=32 time=([0-9]{1,4})ms TTL=[0-9]{1,3}$";
$pattern2="^([0-9]{2})\.([0-9]{2})\.([0-9]{4})-([0-9]{2}):([0-9]{2}):([0-9]{2})> (Request timed out.|Reply from [0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}: Destination host unreachable.)$"
foreach($nextLine in $varArray)
{
if($nextLine -like "* time=*")
{
$ConvertedData+=$nextLine -replace $pattern, "data$ChartCounter.addRow([new Date(`$3, `$2, `$1, `$4, `$5, `$6, 00), `$7, $AvarageRespondTime]);"
}
else
{
$ConvertedData+=$nextLine -replace $pattern2, "data$ChartCounter.addRow([new Date(`$3, `$2, `$1, `$4, `$5, `$6, 00), 0, $AvarageRespondTime]);"
}
}
}
Write-Host $CommandExecuteTime
return $ConvertedData
}
答案 0 :(得分:1)
实际上,你附加的是一个字符串,而不是一个数组,但这也一定会很慢。
改变这个:
$ConvertedData=""
...
foreach($nextLine in $varArray)
{
if($nextLine -like "* time=*")
{
$ConvertedData+=$nextLine -replace ...
}
else
{
$ConvertedData+=$nextLine -replace ...
}
}
进入这个:
$ConvertedData = foreach ($nextLine in $varArray) {
if ($nextLine -like "* time=*") {
$nextLine -replace ...
} else {
$nextLine -replace ...
}
}
$ConvertedData -join "`n"
加快速度。
答案 1 :(得分:1)
首先,正如Ansgar写的那样,你要添加一个字符串而不是一个数组。但两种情况下的问题都是一样的。在.NET中,数组和字符串都是不可变的(数组大小,而不是内容)。每次向数组或字符串追加内容时,系统都会将旧内容复制到新的内存位置,然后附加新数据。
在数组中,如果手动将数组调整为预期的最终大小,则可以解决此问题。调整大小会复制数组, 但它只做一次而不是每次追加。速度差异可以巨大!
让我们考虑以下命令将10000个项附加到空数组:
$a = @(); Measure-Command { for($i = 0; $i -lt 10000; $i++) { $a += $i } }
运行此命令我得到以下结果
Days : 0
Hours : 0
Minutes : 0
Seconds : 3
Milliseconds : 534
Ticks : 35342407
TotalDays : 4,09055636574074E-05
TotalHours : 0,000981733527777778
TotalMinutes : 0,0589040116666667
TotalSeconds : 3,5342407
TotalMilliseconds : 3534,2407
现在考虑以下命令。它首先使用Array对象的Resize静态成员函数调整数组大小,然后使用索引设置10000。
$b = @(); Measure-Command {
[array]::Resize([ref]$b,10000);
for($i = 0; $i -lt 10000; $i++) { $b[$i] = $i }
}
结果是:
Days : 0
Hours : 0
Minutes : 0
Seconds : 0
Milliseconds : 40
Ticks : 402365
TotalDays : 4,65700231481481E-07
TotalHours : 1,11768055555556E-05
TotalMinutes : 0,000670608333333333
TotalSeconds : 0,0402365
TotalMilliseconds : 40,2365
运行时间从3.5秒下降到40毫秒!
你可以将这种技术与Ansgar的技术结合起来。调整数组大小,将结果添加到已调整大小的数组,最后将数组连接到一个巨大的字符串。
一些评论。您可以根据需要多次调用resize,每次都给出新的数组大小。您可以使用数组的Length属性获取当前数组大小。 如果达到限制并且需要更多空间,只需调用调整大小并添加另一个大块。
我不认为你会看到很多改进,但是如果你真的想要尽可能快地去,你应该去MSDN并看看添加到字典类。这是这类思考的推荐类,但从PowerShell中使用它并不容易。
答案 2 :(得分:0)
嗨尝试这样的事情:
$template=@"
{Timelog*:02.01.2017-14:53:54}> {TypeRow:Reply} {Detail:from {IP:8.8.8.8}: {Detailbytes:bytes=32} {Detailtime:time=184ms} {DetailTTL:TTL=57}}
{Timelog*:15.15.2018-20:30:00}> {TypeRow:Reply} {Detail:from {IP:18.28.38.48}: {Detailbytes:bytes=32} {Detailtime:time=184ms} {DetailTTL:TTL=57}}
{Timelog*:02.01.2017-14:53:54}> {TypeRow:Reply} {Detail:from {IP:1.2.345.678}: {Detailbytes:bytes=32} {Detailtime:time=184ms} {DetailTTL:TTL=57}}
{Timelog*:04.01.2017-14:53:59}> {TypeRow:Request} {Detail:{Message:timed out.}}
{Timelog*:03.01.2017-14:54:01}> {TypeRow:Reply} {Detail:from {IP:192.168.2.186}: {Message:Destination host unreachable.}}
{Timelog*:12.01.2017-14:54:05}> {TypeRow:Request} {Detail:{Message:timed out.}}
{Timelog*:02.01.2017-14:54:07}> {TypeRow:Reply} {Detail:from {IP:255.255.255.255}: {Message:Destination host unreachable.}}
"@
get-Content C:\temp\File.txt | ConvertFrom-String -TemplateContent $template |
%{
[pscustomobject]@{
Timelog=$_.Timelog
TypeRow=$_.TypeRow
IP=$_.Message.IP
Detailbytes=if ($_.Detail.Message -ne $null) {''} else {$_.Detail.Detailbytes}
Detailtime=if ($_.Detail.Message -ne $null) {''} else {$_.Detail.Detailtime}
DetailTTL=if ($_.Detail.Message -ne $null) {''} else {$_.Detail.DetailTTL}
Error=$_.Detail.Message
}
} | Format-Table