代码改变世界

为什么Get-ChildItem的执行变慢了?[翻译]

2009-11-08 16:20  @天行健中国元素  阅读(280)  评论(0编辑  收藏

      我们会在缓慢的网络连接时经常遇到这个问题。

      列目录(尤其是在缓慢的网络环境中)的性能是受限于要检索目录信息的.NET API。当前的API中有两方面的限制:

对属性的强制检索

当我们对一个目录进行列目录操作时,通常会显示文件和目录的标准属性:属性(Mode),最后修改时间(LastWriteTime),长度(Length)和文件名。Windows API的内核在基础场景下是经过高度优化的,并从剩余的文件信息中默认返回这些属性。然而,.NET 框架并没有利用这些数据,而是返回网络位置获取所有的文件属性。这种麻烦的方式在每个文件和目录属性获取时增加了网络间往返的时间,这样列目录就更慢了:大量的时间延缓堆积。.NET框架团队将此特性作为.NET 4.0的一部分,当使用这个新特性时将会带来很多的好处。

      在版本二中,即使没有得益于新的.NET API,已然能看到在通配符列目录(包括本地和远程)时带来的巨大改变。

     作为背景,PowerShell通配符是不同于原来的cmd.exe通配符的。比如,在过滤本地文件系统时(通过cmdl.exe通配符公开),PowerShell通配符不能匹配8.3短文件名。PowerShell通配符支持字符串序列,而本地文件系统过滤不支持。因此,PowerShell通配符的操作是在检索所有的文件之后。

      这代价是值得的,过滤本地文件系统(通过-Filter参数)快多了,因为处理的过程是通过Winddows文件系统的。

      在版本二中,有很多方法可以解决这个问题。当提供PowerShell通配符时,尽可能作为本地文件系统过滤器进行转换,并在较小的结果集上应用通配符逻辑。用户可能已经注意到了这些主要是在Tab自动补全中,但已经在常规的通配符列目录中有了很大的改变,尤其是在远程目录中。自从本地过滤操作宝贝作为远程文件系统来操作时,不需要遭受访问文件属性产生的性能瓶颈。在版本一中,可以通过直接指定-Filter参数操作这个问题。如果仍然不能满足你的需要,就可以调用”cmd.exe /c dir”。

枚举API的缺乏

这个问题的出现是因为所列的目录中包含大量的文件。DirectoryInfo.GetFiles()方法返回一个数组。当创建结果列表时,.NET框架会做数组的重组(和复制),将会引起指数级的性能降低:

image

      这也将被在.NET 4.0 updates中解决,通过向目录结果提供允许枚举的API,而不是重新再次取回。如果用户遇到这些局限性,可以再次应用通配符的方法解决。如果依旧未达到你的目标速度,则可以调用“cmd.exe /c <command>”的形式。

为什么没有修复它?

既然发现cmd.exe中存在这些问题,为什么不只是在核心的Windows API中直接操作呢?原因有两点:

    1. PowerShell最核心的原则是提供访问真实的内在.NET对象。如果只是在语义上执行,就需要返回新的对象类型——类似于PSFileInfo和PSDirectoryInfo。V1脚本(或之前的cmdlet)地方得到真实的内在.NET对象将不会正常工作。如果我们添加新的开关(如-Raw),用户就需要修改他们原有的脚本来支持。这样,还不如让他们最好使用现有的cmd /c工作方式。

    2. 这个问题根本上是很短暂的,如果它需要耗费很多年来做,就不需要用户改变代码来适应它了。只需要重新安装一个版本,问题就可以很容易的被解决了。

再次感谢用户的持续反馈。这将帮助我们发现问题,并确认用户能正确的理解它们。

Lee Holmes [MSFT]
Windows PowerShell Development
Microsoft Corporation

发布于 周三, 2009.11.04 8:52 PM by PowerShellTeam

原文地址:http://blogs.msdn.com/powershell/archive/2009/11/04/why-is-get-childitem-so-slow.aspx

 

译者: 天行健
出处:http://fuhj02.cnblogs.com/
版权:本文版权归作者和博客园共有
转载:欢迎转载,为了保存作者的创作热情,请按要求【转载】,谢谢
要求:未经作者同意,必须保留此段声明;必须在文章中给出原文连接;否则必究 法律责任