4

我正在使用 Perl readdir 来获取文件列表,但是,该目录包含超过 250,000 个文件,这导致执行 readdir 的时间很长(超过 4 分钟)并使用超过 80MB 的 RAM。由于这是每 5 分钟重复一次的工作,因此这种延迟时间是不可接受的。

更多信息:另一个作业将填充正在扫描的目录(每天一次)。这个 Perl 脚本负责处理文件。为每个脚本迭代指定一个文件计数,当前每次运行 1000 个。Perl 脚本每 5 分钟运行一次并处理(如果适用)多达 1000 个文件。文件计数限制旨在允许下游处理跟上 Perl 将数据推送到触发复杂工作流程的数据库。

是否有另一种从目录中获取文件名的方法,理想情况下限制为 1000(由变量设置),这将大大提高该脚本的速度?

4

6 回答 6

9

readdir当您说需要几分钟和 80 MB时,您到底是什么意思?你能显示那行特定的代码吗?您是readdir在标量还是列表上下文中使用?

你在做这样的事情:

foreach my $file ( readdir($dir) ) { 
   #do stuff here
}

如果是这种情况,您正在将整个目录列表读入内存。难怪需要很长的时间和大量的内存。

这篇文章的其余部分假设这是问题所在,如果您没有readdir在列表上下文中使用,请忽略文章的其余部分。

解决此问题的方法是使用 while 循环并readdir在标量上下文中使用。

while ( 
    defined( my $file = readdir $dir )
 ) {

    # do stuff.

}

现在您一次只能阅读一项。您也可以添加一个计数器来跟踪您处理的文件数量。

于 2009-04-09T22:34:44.767 回答
7

解决方案可能位于另一端:在填充目录的脚本中......

为什么不创建一个树状结构来存储所有这些文件,这样就有很多目录,每个目录都有可管理数量的文件?

与其创建“mynicefile.txt”,为什么不创建“m/my/mynicefile”或类似的东西?

您的文件系统会为此感谢您(特别是如果您在完成空目录后删除它们)。

于 2009-04-09T21:13:11.640 回答
2

这不完全是您查询的答案,但我认为在同一个目录中有这么多文件对于整体速度来说并不是一件好事(包括文件系统处理添加和删除操作的速度,而不仅仅是列出您看到)。

该设计问题的解决方案是为文件名的每个可能的第一个字母设置子目录,并将所有以该字母开头的文件放在该目录中。如果需要,递归到第二个、第三个等字母。

您可能会在可能的操作中看到明显的速度改进。

于 2009-04-09T21:13:18.520 回答
2

您是说内容通过解压缩 zip 文件到达那里。为什么不只处理 zip 文件而不是在一个目录中创建/使用 250k 的文件?

基本上 - 为了加快速度,您不需要 perl 中的特定内容,而是在文件系统级别。如果您 100% 确定您必须处理目录中的 250k 个文件(我无法想象需要这样的情况) - 您最好找到更好的文件系统来处理它而不是找到perl 中的一些“神奇”模块可以更快地扫描它。

于 2009-04-09T21:33:03.887 回答
1

可能不是。我猜大部分时间是在阅读目录条目。

但是,您可以预处理整个目录列表,每 1000 个条目创建一个文件。然后,您的进程每次都可以执行这些列表文件之一,而不会产生读取整个目录的费用。

您是否尝试过 readdir()通过目录而不进行任何其他处理来获得基线?

于 2009-04-09T21:06:34.057 回答
1

您将无法加快 readdir,但您可以加快监视目录的任务。您可以要求操作系统进行更新——例如,Linux 具有 inotify。这是一篇关于使用它的文章:

http://www.ibm.com/developerworks/linux/library/l-ubuntu-inotify/index.html?ca=drs-

您可以使用 Perl 中的 Inotify:

http://metacpan.org/pod/Linux::Inotify2

不同之处在于您将拥有一个长时间运行的应用程序,而不是由 cron 启动的脚本。在应用程序中,您将保留一个新文件队列(由 inotify 提供)。然后,您设置一个定时器,每 5 分钟响一次,并处理 1000 个项目。之后,控制权返回事件循环,您要么在 5 分钟内醒来并处理 1000 个以上的项目,要么 inotify 向您发送更多文件以添加到队列中。

(顺便说一句,您将需要一个事件循环来处理计时器;我推荐 EV。)

于 2009-04-10T04:13:02.427 回答