DwarFS:快速的高压缩率只读文件系统

2020-11-30 00:53:43

DwarFS是一个只读文件系统,着重于实现非常高的压缩率,尤其是对于非常冗余的数据。

这听起来可能并不令人兴奋,因为如果有多余的话,它应该可以很好地压缩。但是,我发现其他只读压缩文件系统在利用此冗余方面做得不好。请参阅此处以与其他压缩文件系统进行比较。

DwarFS也不影响速度,在我的用例中,我发现它与SquashFS相当或性能更好。在我的主要用例中,DwarFS压缩比SquashFS压缩好一个数量级,构建文件系统的速度快4倍,访问DwarFS上的文件的速度通常更快,并且它占用了无用的CPU资源。

使用相似性哈希函数按相似性将文件聚类,这使得跨文件边界的冗余利用变得更加容易。

跨文件系统块的分段分析,以减小未压缩文件系统的大小。这在使用压缩文件系统时节省了内存,因此有可能允许更高的缓存命中率,因为可以在缓存中保留更多数据。

高度多线程的实现。文件系统创建工具和FUSE驱动程序都能够充分利用系统的许多核心。

我从2013年开始研究DwarFS,主要用例和主要动机是我有数百个不同版本的Perl占用了大约30 GB的磁盘空间,并且我不愿意花费超过10%的硬盘驱动器来维持它们当我碰巧需要它们的时候

直到那时,我一直在使用Cromfs将它们压缩到可管理的大小。但是,构建文件系统映像所花费的时间让我越来越烦,而且使事情变得更糟的是,它通常在大约一个小时后崩溃。

我显然也研究过SquashFS,但是从来没有接近Cromfs的压缩率。

仅凭这一点还不足以让我编写DwarFS,但是大约在同一时间,我非常着迷于更新的C ++标准的最新开发和功能,并真的希望可以从事C ++的业余项目。另外,我很想用FUSE做一些事情。最后但并非最不重要的一点是,我一直在思考压缩文件系统的问题,并且有一些我肯定想尝试的想法。

大部分代码是在2013年编写的,然后我不时进行几次清理,错误修复和重构,但Inever确实使它处在一个让我感到高兴的状态。依赖于Facebook的(非常了不起的)愚蠢的库来构建它太尴尬了,它没有任何文档。

今年再次挖掘该项目时,情况看起来不再像以前那样糟糕。 Folly现在使用CMake进行构建,因此我只是将其作为子模块加入了。可以从应该广泛使用的软件包中满足大多数其他依赖关系。而且我还写了一些基本文档。

它同时使用Boost和Folly,尽管后者作为子模块包含在内,因为很少有发行版为此提供软件包。 Folly本身具有许多依赖性,因此请在此处查看最新列表。

它还使用Facebook Thrift(尤其是冻结的库)以高空间效率,内存可映射和定义明确的格式存储元数据。它也作为子模块包括在内,我们只构建编译器和一个精简的库,其中包含足以使DwarFS正常工作的库。

除此之外,DwarFS实际上仅依赖FUSE3和Folly已经依赖的一组压缩库(即lz4,zstd和liblzma)。

#apt install \ g ++ \ clang \ cmake \ make \ bison \ flex \ pkg-config \ binutils-dev \ libboost-all-dev \ libevent-dev \ libdouble-conversion-dev \ libgoogle-glog-dev \ libgflags-dev \ libiberty-dev \ liblz4-dev \ liblzma-dev \ libzstd-dev \ libsnappy-dev \ libjemalloc-dev \ libssl-dev \ libunwind-dev \ libfmt-dev \ libfuse3-dev \ libsparsehash-dev \ zlib1g-dev

您可以选择clang或g ++,但是至少最近的clangversions会产生更快的代码:

$ hyperfine ./dwarfs_test-*基准测试#1:./dwarfs_test-clang-O2时间(平均值±σ):9.425 s±0.049 s [用户:15.724 s,系统:0.773 s]范围(最小值…最大值):9.373 s …9.523 s 10运行基准测试2:./dwarfs_test-clang-O3时间(平均值±σ):9.328 s±0.045 s [用户:15.593 s,系统:0.791 s]范围(最小…最大):9.277 s…9.418 s 10运行基准3:./ dwarfs_test-gcc-O2时间(平均值±σ):13.798 s±0.035 s [用户:20.161 s,系统:0.767 s]范围(最小…最大):13.731 s…13.852 s 10运行基准4:./ dwarfs_test-gcc-O3时间(平均值±σ):13.223 s±0.034 s [用户:19.576 s,系统:0.769 s]范围(最小…最大):13.176 s…13.278 s 10次运行摘要'./dwarfs_test-clang-O3'比'./dwarfs_test-clang-O2'1.42±0.01倍快于'./dwarfs_test-gcc-O3'1.48±0.01倍于'./dwarfs_test -gcc-O2'$超精细-L prog $(echo ./mkdwarfs-* | tr'',)'{prog} --no-progress --log-level警告-i tree -o / dev / null -C null'基准测试#1:./ mkdwarfs-clang-O2-无进度-日志级别警告-i树-o / dev / null -C空时间(平均值±σ):4.358 s±0.033 s [用户:6.364 s,系统:0.622 s]范围(最小值…最大值):4.321 s…4.408 s 10运行基准测试2:./ mkdwarfs-clang-O3 -no-progress -log级警告-i tree -o / dev / null -C空时间(平均值±σ):4.282 s±0.035 s [用户:6.249 s,系统:0.623 s]范围(最小值…最大值):4.244 s…4.349 s 10次运行基准3:./ mkdwarfs-gcc-O2-否进行-日志级别警告-i树- o / dev / null -C空时间(平均值±σ):6.212 s±0.031 s [用户:8.185 s,系统:0.638 s]范围(最小…最大):6.159 s…6.250 s 10次运行基准4 :。 / mkdwarfs-gcc-O3 --no-progress --log-level警告-i树-o / dev / null -C空时间(平均值±σ):5.740 s±0.037 s [用户:7.742 s,系统:0.645 s]范围(最小值…最大值):5.685 s…5.796 s十次运行摘要'./mkdwarfs-clang-O3 --no-progress --log-level警告-i tree -o / dev / null -C null'跑比'./mkdwarfs-clang-O2 -no-progress -log-level warn -i tree -o / dev / null -C null'快1.02±0.01倍fa ster比'./mkdwarfs-gcc-O3 --no-progress --log级警告-i tree -o / dev / null -C null'1.45±0.01倍比'./mkdwarfs-gcc-O2-不进行--log级警告-i树-o / dev / null -C null'

如果可能,请尝试使用clang作为编译器进行构建,这将使DwarFS明显更快。如果同时安装了gcc和clang,请使用:

要使用实验性Lua支持进行构建,您需要同时安装lua和luabind。后者的维护不是很好,我希望将来摆脱依赖。将-DWITH_LUA = 1添加到cmake命令行以启用Lua支持。

请查看有关mkdwarfsand dwarfs的手册页。 dwarfsck也将被构建和安装,但仍在进行中。

这些测试是在具有64 GiB RAM的Intel®Xeon®CPU D-1528 @ 1.90GHz6核心CPU上完成的。在所有测试期间,系统大部分处于空闲状态。

源目录包含来自284个不同发行版的1139种不同的Perl安装,1,927,501个文件和330,733个目录中的47.65 GiB数据。源目录是从tar存档中刚解压缩到850 EVO 1TB SSD的,因此大多数内容可能都已缓存。

我对SquashFS使用的压缩类型和压缩级别与DwarFS的默认设置相同:

$ time mksquashfs install perl-install.squashfs -comp zstd -Xcompression-level 22并行mksquashfs:使用12个处理器在perl-install.squashfs上创建4.0文件系统,块大小为131072。[============= ================================================== =====-] 2107401/2107401 100%可移动Squashfs 4.0文件系统,zstd压缩,数据块大小131072压缩数据,压缩元数据,压缩片段,压缩xattrs,压缩id重复项已删除文件系统大小4637597.63 KB(4528.90 MB)9.29%未压缩文件系统大小(49922299.04 KB)的Inode表大小19100802字节(18653.13 KB)未压缩的inode表大小的26.06%(73307702字节)目录表大小19128340字节(18680.02 KB)的46.28%未压缩目录表大小(41335540字节)找到重复的文件1780387索引节点数2255794文件数1925061片段数28713符号链接数0设备节点数0 fifo节点数0袜子数et节点0目录数330733 ID数(唯一uids + gids)2 uids数1 mhx(1000)gids数1用户(100)实际69m18.427suser 817m15.199ssys 1m38.237s

$ time ./mkdwarfs -i install -o perl-install.dwarfs02:48:48.592349扫描install02:49:00.603961等待后台扫描程序... 02:50:18.391026分配目录和链接索引节点... 02:50:18.736203查找重复文件... 02:50:28.618659在1782826/1927501中保存了28.2 GiB / 47.65 GiB复制文件02:50:28.618742通过相似性排序144675个inode ... 02:50:29.196793 144675个inode已排序[578ms] 02:50: 29.196877分配文件索引节点... 02:50:29.199317构建元数据... 02:50:29.199403构建块... 02:50:29.199450保存名称和链接... 02:50:29.702547更新名称和链接索引。 ..03:03:45.892033等待块压缩完成... 03:03:45.897608保存块... 03:03:45.924720保存目录... 03:03:49.809202等待压缩完成... 03 :04:31.251687将47.65 GiB压缩为555.7 MiB(ratio = 0.0113884)03:04:31.737918创建的文件系统没有错误[943.1s] ---------------------- -------------------------------------------------- -------扫描/傅nd:330733/330733 dirs,0/0链接,1927501/1927501文件原始大小:47.65 GiB,重复数据删除:28.2 GiB(1782826文件),段:12.42 GiB文件系统:7.027 GiB在450块(754024块,144675/144675 inode)中压缩文件系统:450块/555.7 MiB写入| ========================================= ==================================== |真实15m43.302suser 115m10.704ssys 2m56.544s

因此,在此比较中,mkdwarfs比mksquashfs快4倍以上,在总CPU时间上,实际上比CPU资源少7倍。

$ ls -l perl-install。* fs-rw-r--r-- 1个mhx用户582654491 Nov 29 03:04 perl-install.dwarfs-rw-r--r-- 1个mhx用户4748902400 Nov 25 00: 37个perl-install.squashfs

在压缩率方面,DwarFS文件系统比SquashFS文件系统小8倍以上。使用DwarFS,内容已压缩到其原始大小的1.1%(!)。

DwarFS还具有一个选项,可以使用不同的压缩算法来重新压缩现有文件系统。这很有用,因为它允许使用不同的算法和选项进行相对较快的实验,而无需完全重建文件系统。例如,使用最佳压缩率(-l 9)重新压缩上述文件系统:

$ time ./mkdwarfs --recompress -i perl-install.dwarfs -o perl-lzma.dwarfs -l 903:18:44.670116重写了文件系统[659.4s] --------------- -------------------------------------------------- --------------扫描/找到:0/0个目录,0/0个链接,0/0个文件原始大小:47.65 GiB,重复数据删除:0 B(0个文件),段:0 B文件系统:450块(0块,0/0索引节点)中的7.027 GiB压缩文件系统:450块/457.5 MiB写入| ======================= | ================================== |真实10m59.538suser 120m51.326ssys 1m43.097s $ ls -l perl-*。dwarfs-rw-r--r-- 1个mhx用户582654491 Nov 29 03:04 perl-install.dwarfs-rw-r--r-- 1个mhx用户479756881 Nov 29 03:18 perl-lzma。矮人

这样可以将文件系统的大小再减少18%,使总压缩率低于1%。

就文件系统使用时的速度而言,我所做的一项快速测试是重新安装上面创建的文件系统,并运行每个1139 perl可执行文件以打印其版本。

$ hyperfine -c“ umount mnt” -p“ umount mnt; ./dwarfs perl-install.dwarfs mnt -o cachesize = 1g -o worker = 4; sleep 1” -P procs 5 20 -D 5“ ls -1 mnt / * / * / bin / perl5 * | xargs -d $'\ n'-n1 -P {procs} sh -c'\ $ 0 -v> / dev / null'“基准#1:ls -1 mnt / * / * / bin / perl5 * | xargs -d $'\ n'-n1 -P5 sh -c'$ 0 -v> / dev / null'时间(平均值±σ):4.092 s±0.031 s [用户:2.183 s,系统:4.355 s]范围( min…max):4.022 s…4.122 s 10次运行基准测试2:ls -1 mnt / * / * / bin / perl5 * | xargs -d $'\ n'-n1 -P10 sh -c'$ 0 -v> / dev / null'时间(平均值±σ):2.698 s±0.027 s [用户:1.979 s,系统:3.977 s]范围(最小值…最大值):2.657 s…2.732 s 10次运行基准测试3:ls -1 mnt / * / * / bin / perl5 * | xargs -d $'\ n'-n1 -P15 sh -c'$ 0 -v> / dev / null'时间(平均值±σ):2.341 s±0.029 s [用户:1.883 s,系统:3.794 s]范围( min…max):2.303 s…2.397 s 10次运行基准测试4:ls -1 mnt / * / * / bin / perl5 * | xargs -d $'\ n'-n1 -P20 sh -c'$ 0 -v> / dev / null'时间(平均值±σ):2.207 s±0.037 s [用户:1.818 s,系统:3.673 s]范围(最小…最大):2.163 s…2.278 s 10次运行

这些时间是针对新安装的文件系统上的初始运行,并行运行5、10、15和20个进程。 2.2秒意味着每个Perl二进制文件仅花费大约2毫秒。

以下是在DwarFS(位于mnt)和原始EXT4(位于安装)上后续运行的时间。 DwarFS的速度大约慢15%:

$ hyperfine -P procs 10 20 -D 10 -w1“ ls -1 mnt / * / * / bin / perl5 * | xargs -d $'\ n'-n1 -P {procs} sh -c'\ $ 0 -v > / dev / null'“” ls -1 install / * / * / bin / perl5 * | xargs -d $'\ n'-n1 -P {procs} sh -c'\ $ 0 -v> / dev / null '“基准测试1:ls -1 mnt / * / * / bin / perl5 * | xargs -d $'\ n'-n1 -P10 sh -c'$ 0 -v> / dev / null'时间(平均值±σ):655.8 ms±5.5 ms [用户:1.716 s,系统:2.784 s]范围(最小值…最大值):647.6毫秒…664.3毫秒10次运行基准测试2:ls -1 install / * / * / bin / perl5 * | xargs -d $'\ n'-n1 -P10 sh -c'$ 0 -v> / dev / null'时间(平均值±σ):583.9 ms±5.0 ms [用户:1.715 s,系统:2.773 s]范围( min…max):577.0 ms…592.0 ms 10次运行基准测试3:ls -1 mnt / * / * / bin / perl5 * | xargs -d $'\ n'-n1 -P20 sh -c'$ 0 -v> / dev / null'时间(平均值±σ):638.2 ms±10.7 ms [用户:1.667 s,系统:2.736 s]范围(最小值…最大值):629.1毫秒…658.4毫秒10次运行基准4:ls -1 install / * / * / bin / perl5 * | xargs -d $'\ n'-n1 -P20 sh -c'$ 0 -v> / dev / null'时间(平均值±σ):567.0 ms±3.2 ms [用户:1.684 s,系统:2.719 s]范围(最小…最大):561.5毫秒…570.5毫秒10次运行

$ hyperfine -c“ umount mnt” -p“ umount mnt; ./dwarfs perl-lzma.dwarfs mnt -o cachesize = 1g -o worker = 4; sleep 1” -P procs 5 20 -D 5“ ls -1 mnt / * / * / bin / perl5 * | xargs -d $'\ n'-n1 -P {procs} sh -c'\ $ 0 -v> / dev / null'“基准#1:ls -1 mnt / * / * / bin / perl5 * | xargs -d $'\ n'-n1 -P5 sh -c'$ 0 -v> / dev / null'时间(平均值±σ):20.372 s±0.135 s [用户:2.338 s,系统:4.511 s]范围( min…max):20.208 s…20.601 s 10次运行基准测试2:ls -1 mnt / * / * / bin / perl5 * | xargs -d $'\ n'-n1 -P10 sh -c'$ 0 -v> / dev / null'时间(平均值±σ):13.015 s±0.094 s [用户:2.148 s,系统:4.120 s]范围( min…max):12.863 s…13.144 s 10次运行基准测试3:ls -1 mnt / * / * / bin / perl5 * | xargs -d $'\ n'-n1 -P15 sh -c'$ 0 -v> / dev / null'时间(平均值±σ):11.533 s±0.058 s [用户:2.013 s,系统:3.970 s]范围( min…max):11.469 s…11.649 s 10次运行基准4:ls -1 mnt / * / * / bin / perl5 * | xargs -d $'\ n'-n1 -P20 sh -c'$ 0 -v> / dev / null'时间(平均值±σ):11.402 s±0.095 s [用户:1.906 s,系统:3.787 s]范围( min…max):11.297 s…11.568 s 10次运行

因此,如果您想针对文件系统性能进行优化,则可能要考虑使用zstd而不是lzma。这也是mkdwarfs使用的默认压缩。

在不同的系统上,英特尔®酷睿TM i7-8550U CPU @ 1.80GHz,具有4个内核,我对SquashFS和DwarFS进行了更多测试(仅因为在6核盒子上我的内核不支持zstd在SquashFS中):

hyperfine -c'sudo umount / tmp / perl / install'-p'umount / tmp / perl / install; ./dwarfs perl-install.dwarfs / tmp / perl / install -o cachesize = 1g -o worker = 4;睡眠1'-n dwarfs-zstd“ ls -1 / tmp / perl / install / * / * / bin / perl5 * | xargs -d $'\ n'-n1 -P20 sh -c'\ $ 0 -v> / dev / null'“ -p'sudo umount / tmp / perl / install; sudo mount -t squashfs perl-install.squashfs / tmp / perl / install;睡眠1'-n squashfs-zstd“ ls -1 / tmp / perl / install / * / * / bin / perl5 * | xargs -d $'\ n'-n1 -P20 sh -c'\ $ 0 -v> / dev / null'“ Benchmark#1:dwarfs-zstd时间(平均值±σ):2.071 s±0.372 s [用户:1.727 s,系统:2.866 s]范围(最小…最大):1.711 s…2.532 s 10次运行基准#2:squashfs-zstd时间(平均值±σ):3.668 s±0.070 s [用户:2.173 s,系统:21.287 s]范围(最小值…最大值):3.616 s…3.846 s 10次运行摘要“ dwarfs-zstd”运行比'squashfs-zstd'快1.77±0.32倍

因此,DwarFS的速度几乎是SquashFS的两倍。但更重要的是,SquashFS还使用了更多的CPU能力。但是,上面显示的DwarFS数字显然不包括在侏儒过程中花费的时间,因此我在超精细之外重复了测试:

因此,总共DwarFS使用10.5秒的CPU时间,而SquashFS使用23.5秒,是两倍多。忽略“实时”时间,这仅是我在挂载文件系统之后再次卸载文件系统所花费的时间。

另一个实际测试是在压缩文件系统中构建和测试具有624个不同Perl版本的Perl模块。我使用的模块Tie :: Hash :: Indexed具有一个XS组件,该组件需要C编译器来构建。因此,这确实访问了文件系统中的许多不同内容:

#!/ bin / bash set -euperl = $ 1dir = $(echo“ $ perl” | cut -d / --output-delimiter =--f5,6)rsync -a Tie-Hash-Indexed-0.08 / $ dir / cd $ dir $ 1 Makefile.PL> / dev / null 2>&1make测试> / dev / null 2>&1 cd ..rm -rf $ dir echo $ perl

以下命令将在4个corei7 CPU上并行运行多达8个构建,包括5.10.0和5.33.3之间的所有Perlreleases的调试,优化和线程版本,总共624个perl安装:

$ time ls -1 /tmp/perl/install/*/perl-5.??.?/bin/perl5* |排序-t / -k 8 | xargs -d $'\ n'-P 8 -n 1 ./build.sh

使用干净安装的文件系统进行了测试,以确保高速缓存为空。 ccache已准备好以确保可以从缓存中满足所有编译器的运行要求。使用SquashFS的时间是:

同样,DwarFS使用的原始CPU功耗更少,但就挂钟时间而言,两者之间的差异确实很小。