# Compression levels Reference for `saveUpload.zstdCompressionLevel` in [src/config/AppConfig.ts](src/config/AppConfig.ts). If compression ever turns out to be too slow or too memory hungry on some device, pick a different level from the table below rather than measuring again. The level only affects the uploader. Decompression on the parser backend costs the same at every level. ## What was measured A real Europa Universalis IV save, as the game wrote it: | | | | ------------- | ----------------------------------------------------- | | File | `Ibevar1475_01_02save.eu4`, a zip archive | | Size on disk | **11,502,739 bytes** (11.0 MB) | | Entries | `gamestate` (87,188,760 bytes), `meta`, `ai` | | Input to zstd | **87,342,592 bytes** — the tar of those three entries | So the ratios below are against the original 11.5 MB game archive, and zstd itself is working on the 87 MB of uncompressed save text inside it. Each level ran through the project's own `packSave` in a separate process. The heap columns are the WebAssembly linear memory the encoder and the decoder grow to, read from the instance itself — that is the memory a browser tab spends, and it is what to look at when a device runs out. The machine was a desktop; **a phone is several times slower**, so treat the times as a lower bound. Every level round tripped to the same 87,342,592 byte tar, so each row is correct output rather than merely fast output. ## The table | level | compressed | ratio | compress | decompress | encoder heap | decoder heap | window | | -----: | ------------: | --------: | ---------: | ---------: | -----------: | -----------: | -----: | | 1 | 6,698,281 | 1.72x | 0.54 s | 0.28 s | 8 MB | 8 MB | 1 MB | | 2 | 6,600,451 | 1.74x | 0.54 s | 0.29 s | 8 MB | 8 MB | 1 MB | | 3 | 6,094,116 | 1.89x | 0.57 s | 0.28 s | 8 MB | 8 MB | 2 MB | | 4 | 6,082,362 | 1.89x | 0.61 s | 0.28 s | 8 MB | 8 MB | 2 MB | | 5 | 5,280,090 | 2.18x | 0.92 s | 0.28 s | 8 MB | 8 MB | 2 MB | | 6 | 4,943,366 | 2.33x | 1.13 s | 0.27 s | 8 MB | 8 MB | 2 MB | | 7 | 4,738,515 | 2.43x | 1.27 s | 0.27 s | 9 MB | 8 MB | 2 MB | | 8 | 4,514,853 | 2.55x | 1.52 s | 0.28 s | 9 MB | 8 MB | 2 MB | | 9 | 4,445,368 | 2.59x | 1.51 s | 0.26 s | 16 MB | 8 MB | 4 MB | | 10 | 4,280,567 | 2.69x | 1.74 s | 0.27 s | 26 MB | 8 MB | 4 MB | | **11** | **4,157,684** | **2.77x** | **2.44 s** | **0.32 s** | **26 MB** | **8 MB** | 4 MB | | 12 | 4,155,668 | 2.77x | 2.39 s | 0.27 s | 46 MB | 8 MB | 4 MB | | 13 | 4,257,141 | 2.70x | 3.76 s | 0.27 s | 38 MB | 8 MB | 4 MB | | 14 | 4,108,558 | 2.80x | 5.39 s | 0.28 s | 54 MB | 8 MB | 4 MB | | 15 | 3,968,262 | 2.90x | 8.13 s | 0.33 s | 70 MB | 8 MB | 4 MB | | 16 | 3,862,217 | 2.98x | 11.63 s | 0.26 s | 38 MB | 8 MB | 4 MB | | 17 | 3,733,198 | 3.08x | 12.39 s | 0.26 s | 58 MB | 10 MB | 8 MB | | 18 | 3,705,141 | 3.10x | 15.11 s | 0.26 s | 59 MB | 10 MB | 8 MB | | 19 | 3,449,475 | 3.33x | 27.78 s | 0.28 s | 91 MB | 10 MB | 8 MB | | 20 | 3,344,642 | 3.44x | 28.24 s | 0.27 s | 195 MB | 34 MB | 32 MB | | 21 | 3,285,836 | 3.50x | 33.91 s | 0.30 s | 387 MB | 66 MB | 64 MB | | 22 | 3,245,980 | 3.54x | 49.02 s | 0.30 s | 835 MB | 130 MB | 128 MB | ## Picking a level **11 is the default**, because it is where the curve flattens. It gives good compression in reasonable time. If there will be troubles with this level in the future, just change it in config. ## Reproducing There is no script in the repository for this; it was a one-off measurement. To repeat it, stream a save through `packSave` once per level in a separate process, and read `memory.buffer.byteLength` off the encoder and decoder instances of [@tootallnate/zstd-wasm](https://www.npmjs.com/package/@tootallnate/zstd-wasm) for the heap figures.