我读取了子目录中的数百万个文件来/XX/XX/XX/
处理每个文件的内容。在 PHP 中,我将文件读取为
foreach(glob("/folder/*") as $a){
foreach(glob("$a/*") as $b){
foreach(glob("$b/*") as $c){
foreach(glob("$c/*") as $file){
// Processing
}
}
}
}
问题是这段代码以特定的顺序获取文件,但我只需要以任何可能的顺序读取所有文件(以最少的查找时间)。
有没有办法以某种顺序读取文件(存储在 HDD 扇区上)以减少寻道时间?
PHP 代码是为了展示我是如何结束这里的,并且不期望在 PHP 中找到解决方案。我的问题确实是关于shell
.
更新:
我没有清楚地描述问题。我已经有了完整的文件列表。问题不在于找到它们,而在于读取其内容的最佳顺序以减少寻道时间。
我尝试使用glob
而不是
foreach($files as $file){
$content=file_get_contents($file);
}
希望减少每个文件的查找时间。
正如我之前所说,语言并不重要。我可以用 bash 或 C 重写整个代码。
我重新表述我的问题:读取存储在嵌套文件夹(带有 ext4 的 HDD)中的数百万个文件的内容的最快方法是什么?
答案1
“HDD 扇区”在这里并不重要;文件夹内容的存储和访问方式与存储设备的块结构关系不大(但取决于文件系统)。
你的 PHP 代码效率相当低(我会责怪 PHP - PHP 的标准库使得编写高效的代码变得非常困难),而且也不正确 - 如果有比 4 层文件夹更深的东西(你实际上需要写一些东西)递归通过目录,这不是你这样做的方式)。
无论如何,bash
这很简单:
shopt -s globstar
for file in **/** ; do
echo "${file} found!"
done
但是,如果您需要 PHP 中的文件列表,这确实没有帮助。您不需要通配符来遍历目录 - 事实上,这显然是错误的工具。使用 PHP 的方法列出目录,每当找到目录时,对刚刚找到的目录调用相同的方法。
答案2
我建议find
find /folder/ -mindepth 3 -maxdepth 3 -type f
然后取决于您的处理方式,无论您是否愿意使用-exec
,-execdir
或-print0 | xargs -0
。
find
AFAIK 是检索文件最快的方法之一。
答案3
作为 1967 年以来的计算机程序员,我记得当时连接到小型、慢速计算机的微小、慢速、机械定位磁盘上的文件排序是一个问题。进步消除了这种担忧。更快、更大的计算机,更快、更大的软件(RAM 中的缓冲使磁盘接近 RAM 速度),更快、更大、更智能的磁盘和类似磁盘的对象,以及磁盘驱动程序的进一步发展……很少有人关心文件在磁盘上的实际位置(C/H/S 意义上)。 “现代”磁盘驱动程序重新排序请求以最大限度地减少寻道时间,几十年来一直这样做。
生成文件名列表很棘手。一开始你不需要所有的数百万个名字。
使用find
(读man find
重复读取),或者滚动自己的目录遍历代码。
“目录”是一个d------
在其权限中设置了位的文件。
它包含指向文件或目录的指针。
任何合理的编程语言都会让你访问readdir
接口(man readdir
)。
答案4
我重新表述我的问题:读取存储在嵌套文件夹(带有 ext4 的 HDD)中的几百万个文件的内容的最快方法是什么?
没有比按照文件系统提供给您的顺序更好的顺序了。
以某种顺序排列的文件(存储在硬盘扇区上)
您假设文件在磁盘上以某种方式“排序”,并且它与路径有关。事实并非如此。
文件系统,尤其是 ext2/3/4,具有包含文件描述的磁盘布局(这里的目录实际上只是指向其他文件列表的一些特殊文件),这些描述包含文件名、以及在文件中存储数据的块/范围列表(如果文件不够短,无法容纳元数据)。
现在,这些文件通常是不是如果它们的路径以某种方式相关,则它们在磁盘上是连续的。这根本不会发生:在可用的情况下选择用于新文件数据的可用空间。此外,单个文件本身无法保证连续!存储数据的块不必是一个接一个的。
所以,你真的不能从用户态的角度来看,使其更快:使用文件系统的目的是向使用该文件的程序隐藏它是存储介质上的所有块的事实。
你能做的最好的就是不是对文件重新排序。机会(不能保证!)是在使用系统调用进行交互时从文件系统获取它们的顺序readdir
(无论哪种编程语言,都有一种方法向文件系统询问文件列表)与创建的顺序,这可能是空间分配的顺序,可能是块到索引节点映射的顺序,并且可能在某种程度上对应于磁盘上的顺序。
更重要的是,您从根本上受到以下事实的限制:您试图在单个线程中在 HDD 上执行一些寻道时间密集的操作(如果您可以访问 SSD,则不要这样做),并且文件系统未针对此类进行优化访问权限(您将不是为此找到一个好的经典文件系统,因为这不是经典文件系统需要擅长的)。
因此,从架构上来说,您可以改进您的方法(这就是我说您的 PHP 代码效率低下时的意思:确实如此)。
在发现文件时读取文件内容,而不是稍后读取:元数据与文件数据一样分布在磁盘上,因此跳转到其他位置来读取目录的文件列表与读取文件内容一样具有寻道时间。因此,不要“获取文件列表,然后读取每个文件”,而是这样做
- 获取目录中的第一个条目
- 如果它是一个文件,则开始读取它
(这意味着open
荷兰国际集团- 将文件描述符添加到队列中,
- 在读取队列的单独线程中,
- 在那里,跟踪当前正在处理的文件数量,以及
- 如果它低于某个合理的阈值,则弹出队列的尖端,
fadvise
你想读取整个文件,- 将文件描述符添加到事件中
epoll
,并且 - 在另一个单独的威胁中,使用
epoll_wait
.
切勿让磁盘闲置。让内核有机会充分利用其缓冲区,这样您就不必在磁盘上的同一位置两次查找)。
如果是目录,则转到该目录上的 1- 转到下一个目录。
确保您的文件系统缓冲区足够大。您希望所有文件系统元数据都是可缓存的。在现代 Linux 系统上,您不必做太多事情,只需提供足够的 RAM 即可。
像瘟疫一样避免像 PHP 这样的语言(它们让你很难知道下面执行了哪些系统调用,当系统调用可能导致你完全在其他地方寻找时,那就是你的第一个性能问题)(另外,避免像 PHP 这样的语言)瘟疫,它是糟糕的语言设计的典范,正如你的代码片段非常漂亮地说明的那样!)
如果您需要多次执行此操作,请简单考虑
cp -ar
您的数据转移到新的文件系统(也许不需要外部4,但对于 FS Mark 和其他综合“处理许多文件”问题来说效果很好),因为这将使目录条目处于创建(复制)顺序,然后文件名的顺序实际上更有可能有一些东西使用您的磁盘存储。尽管如此,仍不能保证!
如果这是只读的,有一些方法可以保证您获得特定的顺序:将它们放入只读文件系统中(我喜欢squashfs,它也可以压缩,这可能真的比你想象的更重要)。将该文件系统原始放在磁盘上,而不是放在其他文件系统中!考虑一下您是否真的需要文件访问——文件系统真的是存储您拥有的信息的正确方式吗?或者它可能是关系数据库(sqlite?Postgresql?)或某些面向文档的数据库?