Micro-Optimizing .tar.gz Archives by Changing File Order
justinblank.com
justinblank.com
find * -print | rev | sort | rev |
tar --create --no-recursion --files-from - |
gzip
This clusters file types together and within file types and within that files with the same base name close together.This worked surprisingly well for my use cases, though you can imagine that packing and unpacking times were impacted by the additional head seeks caused by the rather arbitrary order in which this accesses files.
$ tar -zcvf file.tar.gz directory/
$ du -sh directory.tar.gz
57M directory.tar.gz
$ find directory/* -print | rev | sort | rev | \
tar --create --no-recursion --files-from - | \
gzip -c > directory.tar.gz
$ du -sh directory.tar.gz
55M directory.tar.gz
3.51% (2MB) reduction makes many sense here. decorate | sort | undecorate
dance on each line produced by find. Where decorate would add the start of each line the things you want to sort by and undecorate would remove them again.-print is also unnecessary.
Simply use
find |For cases like multiple checkouts of the same repo in a single archive, you want real long range compression like lrzip or zstd --long.
And in the end, using zstd would be a win over DEFLATE every time. Just stop using DEFLATE unless you have to for compatibility with something that can't do zstd.
[0] https://www.snopes.com/fact-check/grandmas-cooking-secret/
It's not as popular yet, yes, but for internal use, including using HTTP content encoding if both your server and client support it, it's great.
.tar.gz felt such a wild step backwards after 7z when I switched from Windows to Linux. Why can't we just introduce links and access rights metadata to 7z or something and use a modern, feature-rich archive format which wouldn't require you to decompress everything just to get a clue if there is a particularly named file in there?
That said, recently I've found that CentOS 8 minimal did not have tar installed. So may be it's worth to start using something modern like zip or even 7z.
The seperation of "group files into an archive" and "compress these bytes" can also be very useful. For example, it means I can play around with the latest and greatest compression formats without needing to wait for a version of tar that has that format built-in.
Overall, I think it appeals to the "unix philosophy" - composing two independent tools (tar and gzip) instead of having one integrated solution.
The "tape drive" part is legacy cruft, certainly, but in practice its removal doesn't improve things enough that people find it worth the compatability break.
I'm not going to claim this is better than the more modern feature-rich archive formats, but hopefully this helps you understand the other side of the argument a little better.
The unix philosophy in this case breaks down when you need to access individual files. One possible "unixy" solution would be .gz.tar as opposed to .tar.gz, but it is not universally supported as .tar.gz is.
tar.gz is ancient
For me, Tar + compression is optimal for most of my use cases - transfer over network, software package distribution, long term storage. In case of transferring over fast network, uncompressed Tar is pretty good [0], and Lz4 compressed is pretty fast too. In case of distributing software, XZ compression is optimal (expensive, good level for small projects). In case of long term storage, Rzip compression is optimal (very expensive, good level for large projects).
Besides the pragmatic considerations, as a software developer & maintainer, I honestly enjoy composability.
Funnily enough, just yesterday I've read the specification of SQLite Archive Files[1]. A pretty cool format and yet - my first question was, "Why is there no support for other compression methods? Like Lz4 for high speed, XZ for high compression?" Please note that, unlike with Tar, extending this standard is pretty much a no-go. Meanwhile a .tar.gz / .tar.xz / .tar.rz / .tar.lz4 is a self-documenting file. You know what to do, and it's easy to write a tool that does the right thing automatically.
I've been implementing a toy project recently, and one of the network communication protocols in it has grown into a poor man's re-implementation of Tar. Having realized that just now, I'll probably yank out several dozen lines of code and replace them with one call to tar on each side. However crufty Tar it might have gotten over decades, it's certainly less buggy than my hand-rolled code.
Lastly, Tar is a much richer format than one might ordinarily expect. For example it supports multiple versions of the same file[2] - and you can add newer version(s) incrementally (append-only file; a much safer operation than re-writting/replacing files). Think a poor man's Git/SQLite/Wiki/whatever. Self-contained, self-documenting, all very nice.
--
[0] uncompressed Tar supports fast seeking. If you expect working with an archive for a while, just create an uncompressed copy. Or a text file index, we don't judge.
[1] https://www.sqlite.org/sqlar.html
[2] https://www.gnu.org/software/tar/manual/html_node/multiple.h...
> binsort <dir> | tar -T- --no-recursion -czf out.tar.gz
> Binsorting the distribution of abiword 2.8.6 (44029103 bytes in 3391 files) on a quadcore CPU takes approx. 12s, and produces a tar.gz more than 14% smaller than without processing.
14% is an amazing improvement.
[0] https://en.wikipedia.org/wiki/LZX [1] http://xavprods.free.fr/lzx/optsmmgs.html
Decompressing using a different algorithm like .bz2 or .xz is noticeably slower, so sticking with gz and shuffling the files might split the difference.
gzip bzip2 xz
1 6.771 24.23 13.251
2 6.581 24.101 12.407
3 6.39 23.955 11.975
4 6.313 24.204 11.801
5 6.153 24.513 11.08
6 6.078 24.768 10.911
7 6.057 23.199 10.781
8 6.033 25.426 10.676
9 6.026 23.486 10.623
so gzip has the fastest decompression.That said, xz is in the ballpark and can be significantly smaller.
[1] https://www.rootusers.com/gzip-vs-bzip2-vs-xz-performance-co...
Also, if you need to use deflate for compatibility, use https://github.com/zlib-ng/zlib-ng , which is substantially faster than either zlib or gzip.
"Arch Linux added support for zstd as a package compression method in October 2019 with the release of the pacman 5.2 package manager, and in January 2020 switched from xz to zstd for the packages in the official repository. Arch uses zstd -c -T0 --ultra -20 -, the size of all compressed packages combined increased by 0.8% (compared to xz), the decompression speed is 1300% faster, decompression memory increased by 50 MiB when using multiple threads, compression memory increases but scales with the number of threads used."
TL;DR zstd is faster, smaller and has a wider range than gzip. For maximum compression, plzip seems to be the best.
[0] https://community.centminmod.com/threads/round-4-compression...
I wonder why adoption of better compression formats is so slow.
A tar file is a concatenation of different 512 bytes of data. The first block contains the metadata of the file, and the next blocks contains the content.
Of those first 512 bytes of data, only the first 100 bytes contains the text.
Trying to play with the orders of files to make 100 bytes of data fit next to each other in block of 512 bytes, it does not seems a successful approach.
gzip use a 32kb windows, that can include up to 64 tar headers. At the very best we are saving 100bytes * 64 => 6400 bytes alright 7kb every 32kb. For an optimal compression of the (32-7)/32 => 78%.
The author says that the standard gzip compressed to 45768 / 337920 => 13%
I believe there are better approaches...
a/index.txt b/index.txt a/img/index.txt a/img/logo.png a/img/test.css a/foo.css
Edit: this is what mannschott's script does.