-
-
Notifications
You must be signed in to change notification settings - Fork 563
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use zstd compression #478
Use zstd compression #478
Comments
It would be amazing, because electron-builder uses lzma for windows nsis targets — expected size 35 MB. AppImage uses deflate and size ~51 MB (even more — because 7za is not used, compression is not good (e.g. 7za can compress the same data to 50 MB (1.5 MB difference))). Yes, we use lzma2 for windows portable and users don't complain that start time is slow, but anyway, for now I decided to not use xz for AppImage because of decompression speed. |
Will AppImages using zstd work on older kernels, such as the ones found in Ubuntu LTS, CentOS or Debian? |
If we would implement zstd, then we would implement it in FUSE, which means it would be independent of the kernel. But we would lose the ability to loop-mount such AppImages using old kernels (is anyone ever doing that, anyway?) |
@develar if it is not too much trouble, could you give the following numbers please?
|
We wouldn't lose it, but it won't work on old systems, right? I assume that on newer systems, it should work out of the box, but on older ones, it'd produce an error message. |
Correct. Sorry, I meant "old kernels". Corrected above. |
Zstandard support has been added to squashfs-tools, so if I am not mistaken nothing is stopping us now from using Zstandard for AppImages. Volunteers for a PR? |
zstd library
squashfs-tools
squashfuse
|
https://github.com/vasi/squashfuse/releases/tag/0.1.101 now contains zstd support. |
We should update it in a PR branch, and check whether it builds with that new version. |
Any updates on this issue? I think by this point, zstd support is pretty universal |
Hi @Ambyjkl looks like no one has done any work on this. First step would be to scientifically prove that it actually provides some advantage over what we have in place now. |
@probonopd sorry I kinda forgot about this issue. From my understanding, using zstd would be a one-line change here, right? |
@Ambyjkl I believe the compression level should be selectable to get a balance between how much space used and how much resources used to compress. |
@denji I suggest you open a new issue with that instead |
@denji how does it compare to SquashFS? From what I read it seems to target extremely redundant data, which a lot of AppImages aren't (likely only contain a few text files and sparse data, while precompressed images and binary take up the bulk). EDIT: after doing a little more reading on it and some of my own tests, DwarFS is really impressive. I got 2x better compression than SquashFS for a given AppImage, both using ZSTD. A big issue is how big it is though, static linking gives >8MB executables which is enormous for this kind of usecase. Compressing the runtime could drop that quite a bit, but it would still require medium to large apps for that weight to break even. @probonopd ZSTD gets significantly better performance at the same or even better compression (>3x read speed at same compression; ~2.7x at max). This gain is definitely enough to warrant adding support, if not defaulting to it. I can send a PR when I have some extra free time if implementing it is the only issue. |
@mgord9518 thanks for taking time to add this :). I got started with adding zstd support here #1091, you can reference that. Do make sure your code is well-tested or communicate properly that tests will be added in the future (or bad things will happen) |
Zstd seems more and more promising nowadays. We should really pick up #1091 again. I'll provide feedback asap. |
Reading the makefile for AppImageKit's runtime, there's a TODO mentioning splitting apart the builds per each compression type (which would be absolutely necessary with ZSTD considering its relatively larger library size), would the preferred method of achieving this be environment variables? Eg: make WITH_ZSTD=1 make WITH_GZIP=1 Etc... |
Maybe, if you have some time, you could benchmark this with different parameters for block sizes and compression levels? And document the result, so that we can pick ideal defaults in upcoming tools. |
Hi, I would like to recompress some appimages I have to zstd to improve the execution time, I did some tests by manually compressing the .AppDir to gzip vs zstd:1 and the difference is quite significant in the decompression speed:
If I try to use appimage tool it tells me that "Only gzip (faster execution, larger files) and xz (slower execution, smaller files) compression is supported at the moment". Sorry is this isn't the right place to ask this. |
Please try the next-generation https://github.com/AppImage/appimagetool/ - does it work with that one? |
Thanks, this one works. Here are some benchmarks: The new Brave zstd appimage is 150 MB while the original appimage is 161 MB. Original:
Zstd:
The extraction time is significantly faster. (Something interesting is that doing a appimage-extract to the zstd appimage doesn't result in the list of files being printed on the terminal, but doing it to the original appimage does print that info). And here are benchmarks of the startup time of the appimage: Original AppImage:
Zstd appimage:
Huh, that's weird. It takes longer to start with zstd. How can I find which compression algo is being used on the original appimage? And do I have to do something other than ./appimagetool-x86_64.AppImage --comp zstd Brave.AppDir when creating zstd appimage that improves the startup time? Also what's the default compression level when using zstd and can it be changed? |
That's weird indeed. I mean, we picked zstandard because it should be faster at runtime... unfortunately I don't know all the answers, more investigation is welcome. At least the algorithm, block size, and compression level play into this. We have to make tradeoffs between image size, runtime speed, and zsync efficiency. To find out the compression algorithm of the first AppImage, you might run it with |
I just checked the source of the appimage, they use gzip: https://github.com/srevinsaju/Brave-AppImage/blob/master/.github/workflows/release.yml
I just went and recreated the steps, this time recompressing the extracted dir to gzip with the older version of appimagetool I have (the newer one tells me that it can only do zstd) and now it took 2.7s to start. So that rules out an issue with recreating the appimage. If you want you can try to replicate my results, I extracted it, renamed it to Brave.AppDir and used appimage tool to turn it into a zstd appimage and compare the startup time of both. What's the default zstd compression level being used in appimagetool? If it is something like 9 maybe that is what causes the increased delay. Lots of testing and benchmark I've seen indicate that using anything higher than 2 is rarely needed, it causes something like a 20% decrease in decompression speed for a 2% gain in compression ratio. |
Yes, we have never really systematically analyzed the optimal compression levels. Looks like we are currently invoking mksquashfs without any particular compression level, so whatever it uses by default gets used: Maybe you'd like to add a different compression level there and compare the results. Thanks for your contribution! |
If it is using the default it means that it is using zstd:3. The difference between 1 and 3 isn't that big that I would think that what causes the near extra 2 second delay. But it would need to be tested either way. Thanks for pointing out what to edit in the code of appimage tool, unfortunately that is beyond my knowledge at this point, the build instructions are for docker (which I don't know how to use) and there isn't an aur appimagetool-git package that I could use as reference to make it. I might test other appimages gzip vs zstd to see if it still takes longer to start with those. EDIT: Doing the same test with the librewolf appimage. Original startup time:
Zstd:
It is also slower with the default zstd compression. |
I wonder if a bigger AppImage, like Overte (>500MiB) would be an interesting benchmark. https://overte.org/downloads.html |
@JulianGro Depends on implementation. Using libdeflate closes the gap but ZSTD is still faster. Libdeflate is 2x as fast as zlib, while zstd is 3x as fast for normal usage. |
Alright, I see. A much easier and quicker way to do some tests would be to
Please let me know if you'd like me to elaborate on how exactly to do that. |
Alright I think I got it. The only thing I'm not sure if I did right was the append part with cat, I did this with the zstd1 squashfs image:
And now runtime became the appimage, its size increased to match the size of the squashfs image and it launches librewolf when run so I assume I got it right. Here are the results for for several appimages, I made several copies of the runtime and appended different squashfs images with different comp levels to each and proceeded to benchmark the startup time of each, I only changed the compression level, didn't change anything else: zstd3 (which is 118MB):
zstd1 (125MB):
zstd6 (110MB):
And for reference, here is the startup time of the original librewolf appimage (likely gpzip) it is 112MB:
This is good, even zstd6 is faster by a considerable amount! And here is the startup time of librewolf zstd appimage, created using I think appimagetool is doing more than just using the default zstd 3 compression level because the resulting appimage is 97 MB in size?! That's indicative that is using a very high compression level.
That explains everything. There is something going on with the appimagetool that causes the very compressed appimage, which on one end is good because it compressed it considerable, however now the application is also considerably slower on startup. Maybe for application like web-browsers the documentation should state to not go too hard on the compression level, already zstd1 is smaller than what a native package of librewolf would be (340 MB) so I only see downsides with using a too high compression level for this type of application. Thanks for the help probono, I hope I did all the steps correctly. Here is how I'm benchmarking the startup time, it is a launcher script that launches the appimage in the same directory and will stop counting once the i3wm window class matches the one of the appimage which for librewolf is 'LibreWolf' (I have i3 configured to automatically focus on new windows once they spawn).
The benchmark script was written by AI, so no idea how terrible it might be, but it has worked well so far haha. Edit: I also tested brave with the mksquashfs and append method, the startup time is 1.63 seconds when using zstd1, very good! |
The main issue this has is its As long as you stay in the realm of the |
@Samueru-sama @probonopd the Luckily it's simple enough to change the level, by passing
|
@probonopd if you want to implement different compression levels, I'd suggest accepting "zstd:N" (just as |
Well, ideally we find the optimal ("balanced") settings and set them for everyone. So that we make the choice, and not every application developer has to think about which compression level to use. |
So in essence you're suggesting making it worse than it already is (because right now I can set all the knobs, including block size, to what I know is good for my specific use case). It is a trend I find in a lot of software nowadays, let's not name names. Have your mythical "balanced" setting, but don't make me break open and re-create the appimage if your compression level, squashfs block size etc don't suit me. |
Frankly I doubt there is an optimal level. It depends on the contents, but also on the purpose and nature of the appimage. The app can start fast or slow independently from the unpacked size (maybe it takes ten seconds to scan the system or connect to a server before it's even usable). The developer might favor a small size for easy downloads, or little compression for a fast startup, and you wouldn't know. Etcetera. Researching the "balanced" setting would be a full time job of surveying your "customers" (appimage developers), and still half of them, by definition won't be pleased with the default. It's your call, but maybe there are more useful things to implement (if anything new gets implemented at this point). |
If you ask me the optimal level is zstd:1, it gives similar comp ratios to gzip while being much faster at decompression. Something similar also happens with zstd is used on Btrfs for filesystem level compression, it is only zstd:1 that makes sense, and sometimes zstd:2 is better under some workloads but the difference is less than 2%. |
@Samueru-sama If you're going to go that low, you might as well use lz4_hc, which also has compression similar to gzip but is about twice as fast as zstd and has a much smaller space overhead in the runtime. zstd shines at mid-high compression levels, so "optimal" is probably in the range of 12-17 (15 is the default level for mksquashfs). So you'd get better compression than gzip and still get substantially faster decompression Developers should definitely be able to choose something else if it provides a better experience for their application though |
How can I benchmark lz4_hc? I only found a github issue where the user was told to use lz4:9 instead. Either way I just did a quick test using the librewolf AppDIr as a compressed tar file:
And the sizes of the files were 86.5MiB for zstd:15, 87.7MiB for zstd:12, 116.7MiB for zstd:1 and 124.8MiB for lz4:9. Which yeah these benchmarks give the idea that zstd:12-15 is good, but as seen in the tests above when actually opening the appimage the default zstd compression of mksquashfs was adding a considerable delay, even slower than gzip. |
If you measure sub-second timings (which have a considerable variance as I can see from the various timings you quoted) use sharkdp/hyperfine. E.g. |
@Samueru-sama What I do is make SquashFS images of various levels, then test reading and other simple file operations. FUSE also caches files, so you get a significant speed-up after the first read. I've never had any issues with lz4_hc, what was the issue you saw about? As for the AppImages, I'm not sure why higher zstd ratios would be so much slower, that shouldn't be the case because using squashfuse I didn't seem to notice any massive slow-downs, certainly not any that would make it slower than gzip. When benchmarking SquashFS compression, tar files should be avoided, they give a good indication as to how fast the decompression itself is as a whole, but speed and file size might be different when compressed into blocks, like in SquashFS. Use mksquashfs then mount with squashfuse and test that instead |
I haven't had issues with lz4_hc (because I've never used it lol), just that I asked you how to benchmark it because when I searched for it the info I got is that I should use lz4:9 instead. I asked you because you said that instead of zstd:1 one should use lz4_hc. I just repeated my tests of zstd (defaults which seems to be zstd 15) vs gzip (and modified the script a little bit) with the librewolf appimage: (Once again I just did here was --appimage-extract the librewolf appimage and made a new zstd appimage using appimagetool using its defaults, the title of the appimage indicates the type in the results)
And just for the heck of it, I decided to make again the zstd1 level one appimage using the method that @mralusw suggested:
And it did make a zstd:1 appimages I think, because this appimage is 117.6 MiB, however when I tested it wasn't faster than the original appimage (gzip), which is odd because in the older tests zstd1 was significantly faster than the others:
So I went and made the appimage again manually using This zstd:1 appimage is 122.5 MiB (No idea why there's a size difference) however it does start up quite fast like it should:
So hmmm yeah, there's something other than the zstd compression level at play here. This is the updated script I'm using, for it to work you would need to do this test using i3wm:
And if you want the appimages I made for these tests, just let know and I'll give you link. Edit: Also if you wonder, here is the startup time of a native librewolf (no appimage):
|
That makes a zstd:3, not a zstd:1. Look at the args. Every appimagetool |
Oh my bad, I did actually change the And just to be extra sure I did make it again this time making sure that it is |
The text was updated successfully, but these errors were encountered: