采用以下两种方法在 tarball 中存储包含大量(数千个)相当小文件的目录:
- 将所有文件存储在
root
tarball 中 - 将目录本身存储在 tarball 中,并将文件存储在目录中。
这会对 tar 的解压速度产生任何性能影响 ( tar -xf
) 吗?
我可以想象第二种方法可能会更快(可能更快),但我不知道 tar 到底是如何工作的;因此我的问题。
评论:
- 对于包装()也可以提出同样的问题,
tar -cf
但这对我来说不太重要。 - 当然,我可以自己运行一些测试,但如果它实际上更快的话,我想要一些理论解释。
答案1
不是理论上的答案,但我想我会进行测试。我有一台运行 FreeBSD 10.3 的 Dell 1955 刀片服务器 - 所以这可能是 bsdtar 特有的。我创建了两个 ZFS 文件系统来保持独立(/zroot/tar1
和/zroot/tar2
),然后使用以下命令生成了 4000 个具有随机内容的 1MB 文件:
for i in {1..4000}; do
dd if=/dev/urandom of=/zroot/tar1/tar_test.$i bs=1M count=1
done
然后我将这 4000 个文件复制到/zroot/tar2/mytar
(因此我们每次都使用完全相同的数据),其中“mytar”是一个目录。
首先,在包含所有“松散”文件的文件系统中,我归档了所有文件,然后删除它们(仅留下 tar 文件),然后取消归档它们。我这样做了五次,次数如下:
tar cf 1.tar * 0.76s user 16.98s system 6% cpu 4:52.68 total
tar cf 1.tar * 0.74s user 16.51s system 5% cpu 4:51.63 total
tar cf 1.tar * 0.94s user 16.19s system 5% cpu 4:55.50 total
tar cf 1.tar * 0.82s user 16.15s system 5% cpu 4:52.72 total
tar cf 1.tar * 0.69s user 16.22s system 5% cpu 4:52.00 total
tar xf 1.tar 0.44s user 10.52s system 3% cpu 4:54.92 total
tar xf 1.tar 0.39s user 10.67s system 3% cpu 5:03.59 total
tar xf 1.tar 0.39s user 10.51s system 3% cpu 4:52.85 total
tar xf 1.tar 0.46s user 10.45s system 3% cpu 5:01.28 total
tar xf 1.tar 0.44s user 10.59s system 3% cpu 5:01.29 total
上次提取后,我删除了 tar 文件并更改为/zroot/tar2
再次执行相同测试的位置,只是这次是在包含相同 4000 个文件的目录上:
tar cf 2.tar mytar 0.72s user 16.51s system 5% cpu 5:25.84 total
tar cf 2.tar mytar 0.61s user 16.19s system 5% cpu 5:18.19 total
tar cf 2.tar mytar 0.68s user 16.14s system 5% cpu 5:01.50 total
tar cf 2.tar mytar 0.65s user 15.87s system 5% cpu 4:41.64 total
tar cf 2.tar mytar 0.68s user 16.71s system 5% cpu 5:07.72 total
tar xf 2.tar 0.42s user 10.39s system 3% cpu 4:57.50 total
tar xf 2.tar 0.41s user 10.41s system 3% cpu 4:50.07 total
tar xf 2.tar 0.47s user 10.26s system 3% cpu 4:57.25 total
tar xf 2.tar 0.58s user 10.50s system 3% cpu 5:00.45 total
tar xf 2.tar 0.40s user 11.34s system 4% cpu 4:50.24 total
平均时间我们得到以下结果:
+===========+=========+===========+
| | Loose | Directory |
+===========+=========+===========+
| Archive | 4:52.91 | 5:06.97 |
+-----------+---------+-----------+
| Unarchive | 4:58.79 | 4:55.1 |
+-----------+---------+-----------+
因此我们可以看到,使用目录稍微改善了文件的取消归档,但初始归档的惩罚稍高一些。
我再次做了同样的事情,但使用 truss 来获取每个操作的摘要,平均我们得到的系统调用所花费的总时间:
+===========+=======+===========+
| | Loose | Directory |
+===========+=======+===========+
| Archive | 04:43 | 04:58 |
+-----------+-------+-----------+
| Unarchive | 04:56 | 04:50 |
+-----------+-------+-----------+
最多的时间花费在 read() 系统调用上(同样,平均):
+===========+=======+===========+
| | Loose | Directory |
+===========+=======+===========+
| Archive | 03:53 | 04:07 |
+-----------+-------+-----------+
| Unarchive | 04:37 | 04:36 |
+-----------+-------+-----------+
取消归档时,最大的好处来自更快的 read() 调用和更快的 lstat() 调用的组合(lstat 类似于 stat,但如果文件是符号链接,则不会跟踪它,而是返回有关符号链接的信息) 。
以下是 lstat() 时间的平均值:
+-------+-------+-----------+
| | Loose | Directory |
+-------+-------+-----------+
| lstat | 8.57 | 0.97 |
+-------+-------+-----------+
我不确定这对你有帮助。但是,对你的问题感兴趣并做了一些研究后,我想我应该分享我所看到的内容,看看是否有人可以进一步研究。
这是每次运行的摘要文件的链接,他们应该感兴趣。
由于完整跟踪的大小(~50MB),我很难将它们上传到任何在线持久位置(paste2.org/pastebin/etc)。
答案2
这很大程度上取决于您使用的文件系统。平面目录在 ext2 和其他需要 O(n) 次查找来检查特定名称的目录条目是否存在的较旧文件系统上速度会很慢。 ext3/4 和其他现代文件系统对较大的目录使用基于树的索引,因此它们只需要 O(log n) 查找时间
对于打包(tar -cf)也可以提出同样的问题,但这对我来说不太重要。
另一方面,Tar 创建很大程度上取决于磁盘 IO 以及实现是否进行预读。小文件会产生大量随机读取,并且单文件预读对小文件不起作用。我已经写了法斯塔作为此用例的专门实现,可优化读取文件的顺序并跨多个文件执行预读。
答案3
与提取所需的总时间相比,提取时间的差异并不显着,至少在规模上(数千个文件)。 tar 格式非常简单:它基本上是标头和文件、标头和文件的串联。因此,当您提取时,tar 只是解脱机数据。特别是它不关心是否覆盖现有文件,因此它不会浪费检查时间。 (具有绝对路径的 tarball 的处理方式略有不同,但这无论如何都是不好的 tar 实践)。