-
Notifications
You must be signed in to change notification settings - Fork 2k
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
Switch MapBlock compression to zstd #10788
Conversation
src/serialization.cpp
Outdated
if(version >= 29) | ||
{ | ||
// map the zlib levels [-1,9] | ||
// the range 1-10 is good enough, 0 is the default (currently 3) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You may find this questionable.
Perhaps, since compress can now call multiple schemes, we ought to come with a general compression scale, perhaps -1 to 100, and then map that whatever range the specific algorithms support.
Or remove the config option altogether and use the defaults....
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yet another option is to always use -1 for zlib, and use the zstd spectrum for the config instead.
zstd has a wide range compressions from very fast to very slow, and perhaps we'll never need a new one again.
Oops. Removed some unrelated changes. |
infostream << "Test: Testing zstd wrappers with a large amount " | ||
"of pseudorandom data" << std::endl; | ||
|
||
u32 size = 500000; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
500.000 because zstd default buf size is 128k.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No longer needed now since I changed the bufsize to 64, but it's still a good test, and takes only a few ms anyway.
I can clearly use some help with the build issues. It builds fine on my Linux box. |
3970e06
to
3ffed00
Compare
Looks like Edit: Yeah, I should have used the legacy APIs. Meh. I will see when I get this to this again. |
c0b5721
to
a9ede5f
Compare
OK. At least it now compiles with the legacy API used... |
8bb1242
to
6e6d36e
Compare
Hah. Older versions of zstd require to initialize a dstream before each decompression. New version do not require that. |
I was actually looking into implementing this myself, but didn't finish it. Some discussion on other optimizations to the MapBlock format: #3946 Something that might be worth looking into with zstd would be a custom dictionary -- it should improve compression on the small bits that we pass to the compressor. We compress each part of the mapblock separately, so the inputs are generally 8KiB or less, which isn't much for a compressor to learn from without a precomputed dictionary. It might also help to compress an entire mapblock instead of compressing nodes, meta, etc separately. That might also improve performance since you only have to include the compressor startup time once (although I think zstd has some significant optimizations in startup time over zlib). I wrote a C++ app that converted Minecraft worlds to a Minetest database, profiled it, and found that it spent most of its CPU time in zlib compressing Minetest blocks, so compression performance is definitely important to database performance. |
Thanks @ShadowNinja. There's also #10671 (you've seen it). It turns out we write a lot of blocks to disk when the only thing changed is the timestamp. I've improved startup somewhat by making the stream context thread local. A pre-computed dictionary is tricky... The data can be only decompressed with that exact same dictionary (unless it is written with the data). In addition to compression I find that ostringstream is unreasonably slow - especially construction, reducing the number of those will also help a lot. Let me think this over. I'd prefer not too many change in single PR, on the other hand each time we change the format we need a protocol bump, so those we be good to group. |
I do not know how to fix the Android and Windows builds. It seems we're downloading pre-compiled artifacts, I do not have the necessary environments to build one for libzstd. |
@ShadowNinja See second commit. For version >= 29 node data and metadata is now compressed together. Can't say I like the code - littered with if-statements, but it works. Edit: This saves another 10% of the size at Zstd-3. |
Yeah, I ended up using a I've pushed what I had completed here: https://github.com/ShadowNinja/minetest/commits/mapblock-v29 Only the serialization is completed, and there are a bunch of other changes on top of the compression, but it might be useful. |
I like that better. (BTW, as I found out the hard way, you cannot be use the new compress2 zstd API if you want this work with zstd 1.3.3.) |
You should continue with your branch. Mine is far from complete, and I'm not working on it right now. |
I removed the node data/metadata combined compression from this patch, since I did not like it. |
@ShadowNinja new version, that compresses the entire block. |
71ab68a
to
22296b9
Compare
Another idea for the meta-data length: Instead of writing a separate ostringstream, one could just leave a 4 byte placeholder, then remember the position before and after writing the metadata ( |
If you can make that work that'd be good but I thought ostringstreams did not support seeking. |
According to the c++ standard I'll post an update soon. |
Yet another alternative is to write the metadata last. That way it can simply be skipped by parsing out the rest (all fix sized) and then skipping to the end. |
Now the MacOS build is broken. :( |
Ahhhh of course why didn't I think of that, we can change the format freely anyway.
This is even easier for minetestmapper and similar tools because they can just stop reading before metadata, static objects or anything else that is uninteresting.
Did you add it here? minetest/.github/workflows/macos.yml Line 37 in 6a1424f
|
Thanks @sfan5 . Update coming soon :) Update: Moving the timestamp makes for a little more ugly code, since it's only written to disk, but not the network. |
I still need to update world_format.txt. |
Alternatively we would document version >= 29 separately and leave <= 28 the way it is. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Works, tested:
- old map loadability
- network compatibility, both ways
- format conforming to expectations
Changes look good. |
This add zstd to Minetest.
The code backward, but not forward compatible:
@sfan5's brotli and @est31's earlier zstd branched helped a lot - although I'm using different zstd APIs.
Questions:
Some stats: (scene with viewing_range of 600, 3700 blocks visible)
Zlib-6 (default), 54MB DB, 90s to generate (dominated by compression time on save)
Zlib-3, 69MB DB, 70s to generate
Zstd-15, 50MB, 108s to load
Zstd-10, 50MB, 70s to load
Zstd-6, 54MB, 67s to load
Zstd-3 (default), 56MB DB, 60s to load
Zstd-1, 55MB DB, 60s to load
Zstd- -1, 62MB, 60s to load
Zstd- -10, 72MB, 60s to load
This shows - as expected - that we get more compression per time from zstd. It also shows that for the tiny things like blocks higher zstd level do not buy much. Perhaps we should default to 1. I'm concluding that zstd below level 3 is no longer the bottelneck. (should confirm with a run with no compression)
For zlib levels other than 3 or 6 do not make much sense, while for zstd level 1 seems to be optimal.
See @ShadowNinja's comment below for more ways to optimize this.
I sporadically watched the map save times. Grew up to 5s for zlib-6, stayed below 2s for zlib-3.
Zstd-10 stayed below 2s, Zstd-1 below 1s.
Similarly Zstd-15 stayed below 5s. Not much improvement there for levels -1 or -10.
(I did not test zstd compression levels larger than 10, or negative values - which are faster but with less compression)