我的目录中有几个文件:
$ ls | wc -l
9376
ls *
有人能解释为什么使用and时有这么大的时间差异吗ls
?
$ time ls > /dev/null
real 0m0.118s
user 0m0.106s
sys 0m0.011s
和
$ time ls * > /dev/null
real 1m32.602s
user 0m0.233s
sys 0m0.438s
好的,这是一个激烈的示例,并且可能会得到增强,因为该目录位于通用并行文件系统(GPFS)上。但我也可以看到本地文件系统的速度显着下降。
编辑:
$ time ls -l > /dev/null
real 0m58.772s
user 0m0.113s
sys 0m0.452s
$ time ls -l * > /dev/null
real 1m19.538s
user 0m0.252s
sys 0m0.461s
我应该补充一点,在我的示例中没有子目录:
$ diff <(ls) <(ls *)
$
答案1
当您ls
不带参数运行时,它只会打开一个目录,读取所有内容,对它们进行排序并打印出来。
当您运行时ls *
,首先 shell 展开*
,这实际上与简单的操作相同ls
,使用当前目录中的所有文件构建一个参数向量并调用ls
.ls
然后必须处理该参数向量和每个参数,并调用access(2)
该文件来检查它是否存在。然后它将打印出与第一个(简单)相同的输出ls
。 shell 对大参数向量和ls
的处理都可能涉及大量小块的内存分配,这可能需要一些时间。但是,由于时间很少sys
,user
而且时间很多real
,所以大部分时间都花在等待磁盘上,而不是使用CPU进行内存分配。
每次调用access(2)
都需要读取文件的索引节点以获取权限信息。这意味着比简单地读取目录要进行更多的磁盘读取和查找。我不知道这些操作在您的 GPFS 上有多昂贵,但正如您所显示的ls -l
与通配符情况具有相似运行时间的比较一样,检索 inode 信息所需的时间似乎占主导地位。如果 GPFS 在每次读取操作上的延迟比本地文件系统稍高,我们预计在这些情况下延迟会更加明显。
通配符大小写和ls -l
50% 之间的差异可以通过磁盘上 inode 的排序来解释。如果索引节点按照与目录中文件名相同的顺序连续排列,并ls -l
在排序之前按目录顺序对文件进行 stat(2) 处理,ls -l
则可能会一次扫描读取大部分索引节点。使用通配符,shell 在将文件名传递给 之前会对文件名进行排序ls
,因此ls
可能会以不同的顺序读取 inode,从而增加更多的磁盘头移动。
应该注意的是,您的time
输出将不包括 shell 扩展通配符所花费的时间。
如果您确实想查看发生了什么,请使用strace(1)
:
strace -o /tmp/ls-star.trace ls *
strace -o /tmp/ls-l-star.trace ls -l *
并查看每种情况下正在执行哪些系统调用。
¹ 我不知道是否access(2)
实际使用过,或者是其他东西,例如stat(2)
.但两者都可能需要索引节点查找(我不确定是否access(file, 0)
会绕过索引节点查找。)