如何让Powershell更快地解析XML或进一步优化我的脚本?

时间:2012-06-30 15:47:04

标签: xml windows performance optimization powershell

我的设置包含700万个XML文件,大小从几KB到多MB不等。总而言之,它是大约180GB的XML文件。我需要执行的工作是分析每个XML文件并确定该文件是否包含字符串<ref>,以及是否不将其从当前包含在Referenceless文件夹中的Chunk文件夹中移出。

我创建的脚本运行得很好,但对于我的目的来说它非常慢。它计划在大约24天内完成对所有700万个文件的分析,每秒大约3个文件。我的剧本中有什么可以改变以获得更多表现吗?

另外,为了使问题更加复杂,我在服务器盒上没有正确的权限来运行.PS1文件,因此需要能够在一个命令中从PowerShell运行脚本。如果我有权限,我会设置权限。

# This script will iterate through the Chunk folders, removing pages that contain no 
# references and putting them into the Referenceless folder.

# Change this variable to start the program on a different chunk. This is the first   
# command to be run in Windows PowerShell. 
$chunknumber = 1
#This while loop is the second command to be run in Windows PowerShell. It will stop after completing Chunk 113.
while($chunknumber -le 113){
#Jumps the terminal to the correct folder.
cd C:\Wiki_Pages
#Creates an index for the chunk being worked on.
$items = Get-ChildItem -Path "Chunk_$chunknumber"
echo "Chunk $chunknumber Indexed"
#Jumps to chunk folder.
cd C:\Wiki_Pages\Chunk_$chunknumber
#Loops through the index. Each entry is one of the pages.
foreach ($page in $items){
#Creates a variable holding the page's content.
$content = Get-Content $page
#If the page has a reference, then it's echoed.
if($content | Select-String "<ref>" -quiet){echo "Referenced!"}
#if the page doesn't have a reference, it's copied to Referenceless then deleted.
else{
Copy-Item $page C:\Wiki_Pages\Referenceless -force
Remove-Item $page -force
echo "Moved to Referenceless!"
}
}
#The chunk number is increased by one and the cycle continues.
$chunknumber = $chunknumber + 1
}

我对PowerShell知之甚少,昨天是我第一次打开程序。

4 个答案:

答案 0 :(得分:4)

您需要将-ReadCount 0参数添加到Get-Content命令中以加速它们(它有很大帮助)。我从this一篇很棒的文章中了解到了这一提示,该文章显示在整个文件的内容上运行foreach比尝试通过管道解析它更快。

此外,您可以使用Set-ExecutionPolicy Bypass -Scope Process在当前的Powershell会话中运行脚本,而无需额外的权限!

答案 1 :(得分:2)

PowerShell管道可能明显慢于本机系统调用。

PowerShell: pipeline performance

在本文中,在PowerShell和传统的Windows命令提示符上执行的两个等效命令之间执行性能测试。

PS> grep [0-9] numbers.txt | wc -l > $null
CMD> cmd /c "grep [0-9] numbers.txt | wc -l > nul"

以下是其输出的示例。

PS C:\temp> 1..5 | % { .\perf.ps1 ([Math]::Pow(10, $_)) }

10 iterations

   30 ms  (   0 lines / ms)  grep in PS
   15 ms  (   1 lines / ms)  grep in cmd.exe

100 iterations

   28 ms  (   4 lines / ms)  grep in PS
   12 ms  (   8 lines / ms)  grep in cmd.exe

1000 iterations

  147 ms  (   7 lines / ms)  grep in PS
   11 ms  (  89 lines / ms)  grep in cmd.exe

10000 iterations

 1347 ms  (   7 lines / ms)  grep in PS
   13 ms  ( 786 lines / ms)  grep in cmd.exe

100000 iterations

13410 ms  (   7 lines / ms)  grep in PS
   22 ms  (4580 lines / ms)  grep in cmd.exe

编辑:这个问题的原始答案提到了管道性能以及其他一些建议。为了使这篇文章简明扼要,我删除了其他与管道性能无关的建议。

答案 2 :(得分:1)

在开始优化之前,您需要准确确定需要优化的位置。您是否受I / O限制(读取每个文件需要多长时间)?内存限制(可能不是)? CPU限制(搜索内容的时间)?

你说这些是XML文件;你测试过将文件读入XML对象(而不是纯文本),并通过XPath定位<ref>节点吗?然后你会:

$content = [xml](Get-Content $page)
#If the page has a reference, then it's echoed.
if($content.SelectSingleNode("//ref") -quiet){echo "Referenced!"}

如果你有CPU,内存和要备用的I / O资源,您可以通过并行搜索多个文件来看到一些改进。有关并行运行多个作业的信息,请参阅this discussion。显然你不能同时运行大量的数字,但通过一些测试你可以找到最佳位置(可能在3-5附近)。 foreach ($page in $items){内的所有内容都是作业的脚本块。

答案 3 :(得分:0)

我将尝试使用Start-Job cmdlet一次解析5个文件。有很多关于PowerShell Jobs的优秀文章。如果由于某些原因没有帮助,并且您遇到I / O或实际资源瓶颈,您甚至可以使用Start-Job和WinRM来启动其他计算机上的工作。