2

Take the following two approaches of storing a directory with a lot (several thousands) of rather small files inside a tarball:

  1. Store all the files in the root of the tarball
  2. Store the directory itself in the tarball, with the files inside the directory.

Would this have any performance effects on how fast the tar is unpacked (tar -xf)?

I can imagine that the second approach could be faster (possibly much faster), but I'm not aware of how tar works exactly; hence my question.

Remarks:

  • Same question can be asked for packaging (tar -cf) but this is of less importance to me.
  • Off course I can run some tests myself, but I would like some theoretical explanation if it's actually faster.
2

Not a theoretical answer, but I thought I'd run the tests. I've got a Dell 1955 blade running FreeBSD 10.3 - so this may be specific to bsdtar. I created two ZFS file systems to keep things separate (/zroot/tar1 and /zroot/tar2), and then generated 4000 1MB files with random content using the following:

for i in {1..4000}; do
    dd if=/dev/urandom of=/zroot/tar1/tar_test.$i bs=1M count=1
done

I then copied these 4000 files to /zroot/tar2/mytar (so we use exactly the same data each time), with "mytar" being a directory.


First in the file system with all the 'loose' files, I archived all of the files, then removed them (leaving just the tar file), then unarchived them. I did this five times and the times are shown below:

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

After the last extraction I removed the tar file and changed to /zroot/tar2 where I performed the same tests again, only this time on a directory containing the same 4000 files:

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

Averaging the times out we get the below:

+===========+=========+===========+
|           |  Loose  | Directory |
+===========+=========+===========+
| Archive   | 4:52.91 | 5:06.97   |
+-----------+---------+-----------+
| Unarchive | 4:58.79 | 4:55.1    |
+-----------+---------+-----------+

So we can see that using a directory marginally improves unarchiving the files, with a slightly higher penalty on the initial archiving.


I did the same thing again, but used truss to get a summary of each operation, averaging the total time spent in syscalls we get:

+===========+=======+===========+
|           | Loose | Directory |
+===========+=======+===========+
| Archive   | 04:43 | 04:58     |
+-----------+-------+-----------+
| Unarchive | 04:56 | 04:50     |
+-----------+-------+-----------+

The most time is spent in the read() syscall (again, averaged):

+===========+=======+===========+
|           | Loose | Directory |
+===========+=======+===========+
| Archive   | 03:53 | 04:07     |
+-----------+-------+-----------+
| Unarchive | 04:37 | 04:36     |
+-----------+-------+-----------+

When unarchiving, the biggest wins come from a combination faster read() calls, and faster lstat() calls (lstat is similar to stat, but if the file is a symlink then it isn't followed, instead returning information about the symlink).

Here are the lstat() times, averaged:

+-------+-------+-----------+
|       | Loose | Directory |
+-------+-------+-----------+
| lstat |  8.57 |      0.97 |
+-------+-------+-----------+ 

I'm not sure this helps you at all. But having become intrigued in your question have having done a bit of research, I thought I'd share what I had to see if anyone could take it further.

Here's a link to the summary files from each run, should they be of interest.

Due to the size of the full traces (~50MB), I'm having difficulties uploading them to any persistent place online (paste2.org/pastebin/etc).

  • You might want to try github gists, I think they have a higher file size limit (cannot confirm) but also will also allow you to group multiple files with a single link. – Michael Daffin Sep 6 '16 at 10:32
  • @MichaelDaffin that was my hope, but the page doesn't seem to handle files this big...Maybe it's this laptop though...I'll give it another go. I've uploaded all the summaries into a single gist. – forquare Sep 6 '16 at 10:36
1

This largely depends on the filesystem you are using. A flat directory will be slow on ext2 and other older filesystems that need O(n) lookups to check whether a directory entry of a particular name exists. ext3/4 and other modern filesystems use tree based indexes for larger directories, so they only need O(log n) lookup time

Same question can be asked for packaging (tar -cf) but this is of less importance to me.

Tar creation on the other hand largely depends on disk IO and whether the implementation does readaheads. Small files incur a lot of random reads and single-file readahead does not work effectively on small files. I have written fastar as a specialized implementation for this use-case that optimizes the order in which the files are read and performs readaheads across multiple files.

0

The difference in extraction time is not significant compared to the total time the extraction takes, at least ta scale (thousands of files). The tar format is surprisingly simple: it s basically a concatenation of header and file, header and file. So when you extract, tar just unspools the data. In particular it does not care if it is overwriting an existing file, so it doesn't burn time checking. (tarballs with absolute paths are treated slightly differently, but thats bad tar practice anyways).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.