Benchmarking compression algorithms
Published
tl; dr: brotli on max-settings for static files, otherwise zstandard on level 1 up to 3.
I wanted to know what compression algorithm at which level would perform the best in a real world scenario.
For files compressed ahead of time, it’s mostly the resulting compressed size that matters. That’s because transfer time is much, much higher than decompression time, even on a fast connection.
But what about dynamic assets? How much does decompression time matter? How much effort/time should the server spend on compression before starting to send the file?
The setup
I collected some minified html, css and javascript files with different sizes, from 3 KiB up to over 660 KiB.
3 KiB plausible.js
7 KiB sqlite.org.html
10 KiB water-dark.min.css
22 KiB water.min.css
38 KiB picnic.min.css
50 KiB google-analytics.js
50 KiB bootstrap-grid.min.css
164 KiB digitec.ch.html
192 KiB googletagmanager.js
226 KiB bootstrap.min.css
662 KiB bulma.min.cssNext i prepared a nginx configuration to serve the files with different compression algorithms and levels.
I used the three most common compression algorithms in use on the web:
gzip, brotli and zstandard.nginx configuration file
# [...]
http {
# [...]
gzip_types *;
brotli_types *;
zstd_types *;
server {
root /srv/www;
# [...]
location /gzip {
gzip on;
location /gzip/1/ { alias /srv/www/; gzip_comp_level 1; }
# [...]
location /gzip/9/ { alias /srv/www/; gzip_comp_level 9; }
}
location /brotli {
brotli on;
location /brotli/1/ { alias /srv/www/; brotli_comp_level 1; }
# [...]
location /brotli/11/ { alias /srv/www/; brotli_comp_level 11; }
}
location /zstd {
zstd on;
location /zstd/1/ { alias /srv/www/; zstd_comp_level 1; }
# [...]
location /zstd/19/ { alias /srv/www/; zstd_comp_level 19; }
}
}
}
The i created a simple rust program which downloads and decompresses the files repeatedly and trackes the compressed size and latency (compression + transfer + decompression).
The measurements
For files compressed ahead of time we will just look at the resulting compressed size, as this is most important.
| file | uncompressed | gzip-9 | brotli-11 | zstd-19 |
|---|---|---|---|---|
| plausible.js | 2.93 KiB | 1.29 KiB | 1.12 KiB | 1.30 KiB |
| sqlite.org.html | 8.67 KiB | 2.78 KiB | 2.13 KiB | 2.72 KiB |
| water-dark.min.css | 9.76 KiB | 2.68 KiB | 2.31 KiB | 2.64 KiB |
| water.min.css | 22.1 KiB | 3.47 KiB | 3.01 KiB | 3.35 KiB |
| picnic.min.css | 38.6 KiB | 7.16 KiB | 5.30 KiB | 6.25 KiB |
| bootstrap-grid.min.css | 50.6 KiB | 5.78 KiB | 2.71 KiB | 4.33 KiB |
| google-analytics.js | 51.1 KiB | 20.3 KiB | 18.1 KiB | 19.4 KiB |
| digitec.ch.html | 168 KiB | 31.1 KiB | 24.2 KiB | 26.7 KiB |
| googletagmanager.js | 199 KiB | 74.0 KiB | 63.1 KiB | 67.6 KiB |
| bootstrap.min.css | 227 KiB | 30.1 KiB | 22.4 KiB | 25.6 KiB |
| bulma.min.css | 662 KiB | 63.3 KiB | 35.6 KiB | 40.1 KiB |
file size for all algorithms
| file | uncompressed | gzip-1 | gzip-2 | gzip-3 | gzip-4 | gzip-5 | gzip-6 | gzip-7 | gzip-8 | gzip-9 | brotli-1 | brotli-2 | brotli-3 | brotli-4 | brotli-5 | brotli-6 | brotli-7 | brotli-8 | brotli-9 | brotli-10 | brotli-11 | zstd-1 | zstd-2 | zstd-3 | zstd-4 | zstd-5 | zstd-6 | zstd-7 | zstd-8 | zstd-9 | zstd-10 | zstd-11 | zstd-12 | zstd-13 | zstd-14 | zstd-15 | zstd-16 | zstd-17 | zstd-18 | zstd-19 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| plausible.js | 2.93 KiB | 1.35 KiB | 1.33 KiB | 1.33 KiB | 1.30 KiB | 1.29 KiB | 1.29 KiB | 1.29 KiB | 1.29 KiB | 1.29 KiB | 1.39 KiB | 1.40 KiB | 1.34 KiB | 1.25 KiB | 1.18 KiB | 1.18 KiB | 1.18 KiB | 1.18 KiB | 1.18 KiB | 1.13 KiB | 1.12 KiB | 1.39 KiB | 1.37 KiB | 1.34 KiB | 1.34 KiB | 1.33 KiB | 1.32 KiB | 1.32 KiB | 1.32 KiB | 1.32 KiB | 1.32 KiB | 1.32 KiB | 1.32 KiB | 1.32 KiB | 1.32 KiB | 1.32 KiB | 1.32 KiB | 1.31 KiB | 1.30 KiB | 1.30 KiB |
| sqlite.org.html | 8.67 KiB | 2.99 KiB | 2.95 KiB | 2.93 KiB | 2.82 KiB | 2.78 KiB | 2.78 KiB | 2.78 KiB | 2.78 KiB | 2.78 KiB | 3.05 KiB | 2.94 KiB | 2.90 KiB | 2.63 KiB | 2.44 KiB | 2.45 KiB | 2.43 KiB | 2.44 KiB | 2.44 KiB | 2.20 KiB | 2.13 KiB | 3.05 KiB | 2.98 KiB | 2.91 KiB | 2.91 KiB | 2.85 KiB | 2.84 KiB | 2.83 KiB | 2.83 KiB | 2.83 KiB | 2.83 KiB | 2.83 KiB | 2.83 KiB | 2.83 KiB | 2.83 KiB | 2.83 KiB | 2.81 KiB | 2.75 KiB | 2.73 KiB | 2.72 KiB |
| water-dark.min.css | 9.76 KiB | 3.05 KiB | 2.94 KiB | 2.91 KiB | 2.74 KiB | 2.69 KiB | 2.68 KiB | 2.68 KiB | 2.68 KiB | 2.68 KiB | 3.16 KiB | 3.01 KiB | 2.90 KiB | 2.70 KiB | 2.47 KiB | 2.47 KiB | 2.46 KiB | 2.46 KiB | 2.46 KiB | 2.36 KiB | 2.31 KiB | 3.08 KiB | 3.03 KiB | 2.89 KiB | 2.89 KiB | 2.78 KiB | 2.75 KiB | 2.74 KiB | 2.74 KiB | 2.74 KiB | 2.73 KiB | 2.72 KiB | 2.72 KiB | 2.72 KiB | 2.72 KiB | 2.72 KiB | 2.69 KiB | 2.65 KiB | 2.64 KiB | 2.64 KiB |
| water.min.css | 22.1 KiB | 4.09 KiB | 3.99 KiB | 3.89 KiB | 3.68 KiB | 3.53 KiB | 3.49 KiB | 3.48 KiB | 3.47 KiB | 3.47 KiB | 4.21 KiB | 3.94 KiB | 3.75 KiB | 3.53 KiB | 3.24 KiB | 3.22 KiB | 3.20 KiB | 3.20 KiB | 3.19 KiB | 3.10 KiB | 3.01 KiB | 3.95 KiB | 3.91 KiB | 3.72 KiB | 3.72 KiB | 3.56 KiB | 3.52 KiB | 3.49 KiB | 3.48 KiB | 3.48 KiB | 3.48 KiB | 3.47 KiB | 3.47 KiB | 3.46 KiB | 3.46 KiB | 3.46 KiB | 3.40 KiB | 3.36 KiB | 3.37 KiB | 3.35 KiB |
| picnic.min.css | 38.6 KiB | 8.37 KiB | 8.26 KiB | 8.05 KiB | 7.62 KiB | 7.36 KiB | 7.25 KiB | 7.16 KiB | 7.16 KiB | 7.16 KiB | 8.74 KiB | 6.80 KiB | 6.71 KiB | 6.38 KiB | 5.93 KiB | 5.93 KiB | 5.93 KiB | 5.93 KiB | 5.91 KiB | 5.42 KiB | 5.30 KiB | 7.47 KiB | 7.30 KiB | 7.03 KiB | 7.04 KiB | 6.78 KiB | 6.64 KiB | 6.60 KiB | 6.59 KiB | 6.59 KiB | 6.59 KiB | 6.59 KiB | 6.59 KiB | 6.56 KiB | 6.57 KiB | 6.57 KiB | 6.37 KiB | 6.27 KiB | 6.26 KiB | 6.25 KiB |
| bootstrap-grid.min.css | 50.6 KiB | 8.68 KiB | 7.48 KiB | 6.44 KiB | 7.05 KiB | 6.04 KiB | 5.96 KiB | 5.78 KiB | 5.78 KiB | 5.78 KiB | 9.45 KiB | 6.92 KiB | 6.54 KiB | 6.26 KiB | 4.37 KiB | 4.25 KiB | 4.21 KiB | 4.21 KiB | 4.16 KiB | 2.80 KiB | 2.71 KiB | 5.96 KiB | 6.05 KiB | 6.02 KiB | 6.02 KiB | 5.43 KiB | 5.10 KiB | 5.12 KiB | 4.34 KiB | 4.34 KiB | 4.34 KiB | 4.34 KiB | 4.34 KiB | 4.34 KiB | 4.34 KiB | 4.34 KiB | 4.13 KiB | 4.08 KiB | 4.33 KiB | 4.33 KiB |
| google-analytics.js | 51.1 KiB | 22.8 KiB | 22.3 KiB | 21.9 KiB | 20.9 KiB | 20.4 KiB | 20.3 KiB | 20.3 KiB | 20.3 KiB | 20.3 KiB | 24.1 KiB | 21.5 KiB | 21.4 KiB | 20.9 KiB | 19.7 KiB | 19.7 KiB | 19.6 KiB | 19.6 KiB | 19.6 KiB | 18.5 KiB | 18.1 KiB | 23.5 KiB | 22.4 KiB | 21.5 KiB | 21.4 KiB | 21.1 KiB | 20.8 KiB | 20.7 KiB | 20.6 KiB | 20.6 KiB | 20.5 KiB | 20.5 KiB | 20.5 KiB | 20.5 KiB | 20.5 KiB | 20.5 KiB | 20.2 KiB | 19.7 KiB | 19.5 KiB | 19.4 KiB |
| digitec.ch.html | 168 KiB | 36.5 KiB | 35.8 KiB | 35.2 KiB | 32.9 KiB | 31.8 KiB | 31.3 KiB | 31.2 KiB | 31.1 KiB | 31.1 KiB | 40.0 KiB | 30.1 KiB | 29.4 KiB | 28.0 KiB | 26.7 KiB | 26.6 KiB | 26.6 KiB | 26.5 KiB | 26.4 KiB | 24.5 KiB | 24.2 KiB | 31.3 KiB | 30.6 KiB | 29.8 KiB | 29.8 KiB | 28.5 KiB | 28.0 KiB | 27.9 KiB | 27.8 KiB | 27.8 KiB | 27.7 KiB | 27.6 KiB | 27.6 KiB | 27.6 KiB | 27.6 KiB | 27.6 KiB | 27.4 KiB | 27.1 KiB | 26.9 KiB | 26.7 KiB |
| googletagmanager.js | 199 KiB | 83.7 KiB | 81.7 KiB | 80.4 KiB | 76.4 KiB | 74.6 KiB | 74.1 KiB | 74.0 KiB | 74.0 KiB | 74.0 KiB | 87.9 KiB | 76.5 KiB | 75.9 KiB | 74.2 KiB | 69.8 KiB | 69.3 KiB | 69.0 KiB | 68.8 KiB | 68.7 KiB | 63.8 KiB | 63.1 KiB | 83.2 KiB | 79.2 KiB | 75.8 KiB | 75.6 KiB | 73.9 KiB | 72.4 KiB | 72.1 KiB | 71.7 KiB | 71.7 KiB | 71.5 KiB | 71.4 KiB | 71.4 KiB | 71.2 KiB | 71.1 KiB | 71.1 KiB | 70.0 KiB | 68.5 KiB | 67.6 KiB | 67.6 KiB |
| bootstrap.min.css | 227 KiB | 40.8 KiB | 38.0 KiB | 35.6 KiB | 33.5 KiB | 31.1 KiB | 30.4 KiB | 30.2 KiB | 30.1 KiB | 30.1 KiB | 44.3 KiB | 35.7 KiB | 34.0 KiB | 32.3 KiB | 27.6 KiB | 26.8 KiB | 26.3 KiB | 26.2 KiB | 25.9 KiB | 22.9 KiB | 22.4 KiB | 34.6 KiB | 34.6 KiB | 33.5 KiB | 33.5 KiB | 30.3 KiB | 29.0 KiB | 28.6 KiB | 27.5 KiB | 27.5 KiB | 27.2 KiB | 27.0 KiB | 27.0 KiB | 27.0 KiB | 26.9 KiB | 26.9 KiB | 26.1 KiB | 25.9 KiB | 25.7 KiB | 25.6 KiB |
| bulma.min.css | 662 KiB | 85.2 KiB | 78.9 KiB | 75.1 KiB | 70.9 KiB | 66.3 KiB | 64.4 KiB | 63.5 KiB | 63.3 KiB | 63.3 KiB | 89.3 KiB | 54.8 KiB | 53.4 KiB | 51.2 KiB | 43.4 KiB | 42.7 KiB | 42.2 KiB | 42.0 KiB | 41.4 KiB | 37.4 KiB | 35.6 KiB | 56.0 KiB | 56.1 KiB | 53.3 KiB | 53.5 KiB | 49.1 KiB | 46.1 KiB | 45.5 KiB | 45.4 KiB | 45.4 KiB | 45.4 KiB | 45.3 KiB | 45.3 KiB | 45.1 KiB | 45.0 KiB | 45.2 KiB | 41.6 KiB | 41.0 KiB | 40.5 KiB | 40.1 KiB |
As expected, brotli is just unbelievably effective. Zstandard is not much behind though. Brotli uses a static dictionary, prepopulated with over 13 thousand commonly appearing words, which is part of why it’s so effective.
For files compressed on the fly, the answer is not that clear.
total time for all algorithms
file uncompressed gzip-1 gzip-2 gzip-3 gzip-4 gzip-5 gzip-6 gzip-7 gzip-8 gzip-9 brotli-1 brotli-2 brotli-3 brotli-4 brotli-5 brotli-6 brotli-7 brotli-8 brotli-9 brotli-10 brotli-11 zstd-1 zstd-2 zstd-3 zstd-4 zstd-5 zstd-6 zstd-7 zstd-8 zstd-9 zstd-10 zstd-11 zstd-12 zstd-13 zstd-14 zstd-15 zstd-16 zstd-17 zstd-18 zstd-19 plausible.js 11.619943ms 11.808887ms 11.828168ms 11.887826ms 11.938558ms 11.816462ms 11.899776ms 11.845929ms 11.824042ms 11.821352ms 11.805312ms 11.941513ms 12.009011ms 12.308724ms 12.314365ms 12.128461ms 12.211825ms 12.271057ms 12.844309ms 14.47249ms 16.917388ms 11.885839ms 12.027642ms 12.337311ms 12.795683ms 12.980376ms 13.39223ms 13.554558ms 13.441212ms 14.257149ms 15.283307ms 15.091374ms 16.543376ms 15.485577ms 16.672286ms 18.470996ms 15.473681ms 16.806588ms 16.780712ms 19.716109ms sqlite.org.html 11.896281ms 11.999786ms 12.107881ms 11.852398ms 11.937442ms 11.967814ms 12.160107ms 11.995229ms 12.131341ms 12.093019ms 11.946293ms 12.09578ms 12.187149ms 12.582793ms 12.369373ms 12.456412ms 12.437171ms 12.366788ms 12.965975ms 17.046564ms 23.192753ms 11.864855ms 11.993481ms 12.40691ms 13.024409ms 13.232708ms 13.332618ms 13.519156ms 13.605585ms 14.381106ms 15.078343ms 15.345065ms 16.605569ms 15.636154ms 17.221632ms 18.743018ms 15.817602ms 17.313972ms 17.792499ms 21.298826ms water-dark.min.css 11.920487ms 12.00477ms 11.965274ms 12.031768ms 11.971994ms 12.241136ms 12.009806ms 12.120896ms 12.075449ms 12.082329ms 11.999256ms 12.141022ms 12.147362ms 12.493974ms 12.434115ms 12.350541ms 12.497924ms 12.468207ms 13.110587ms 17.710049ms 25.724395ms 11.838673ms 12.093025ms 12.512764ms 12.908538ms 13.323813ms 13.277344ms 13.665522ms 13.696234ms 14.485826ms 15.33992ms 15.305163ms 16.907293ms 15.64939ms 17.179931ms 18.647723ms 16.314355ms 17.788803ms 18.557019ms 22.407058ms water.min.css 12.146983ms 12.045917ms 12.006366ms 11.99172ms 12.163373ms 12.171594ms 12.256118ms 12.251753ms 12.40637ms 12.236262ms 11.991215ms 12.131451ms 12.308375ms 12.654301ms 12.75408ms 12.614794ms 12.707044ms 12.896542ms 13.27724ms 22.232304ms 41.078478ms 12.019381ms 12.168168ms 12.469473ms 13.090126ms 13.202491ms 13.277604ms 13.571314ms 13.69971ms 14.645887ms 15.244051ms 15.274248ms 17.038144ms 15.831688ms 17.625991ms 19.23095ms 16.974421ms 18.618601ms 19.397243ms 28.905188ms picnic.min.css 12.42407ms 12.381869ms 12.446761ms 12.488598ms 12.553681ms 12.655106ms 12.837914ms 12.886832ms 13.155399ms 13.206876ms 12.362458ms 12.451126ms 12.509389ms 13.023663ms 13.256518ms 13.277815ms 13.502026ms 13.660663ms 14.171841ms 30.24665ms 62.177784ms 12.08518ms 12.3156ms 12.488123ms 13.364944ms 13.648307ms 13.83309ms 13.983032ms 14.069401ms 15.018424ms 16.166632ms 16.001856ms 17.567433ms 16.509669ms 17.903099ms 19.530414ms 19.264757ms 21.387781ms 24.376854ms 31.873336ms bootstrap-grid.min.css 12.585192ms 12.490998ms 12.374403ms 12.392829ms 12.576842ms 12.580463ms 12.985222ms 13.067136ms 13.329277ms 13.309326ms 12.390703ms 12.570632ms 12.564277ms 13.022498ms 13.248689ms 13.249359ms 13.334752ms 13.368859ms 14.015428ms 34.755039ms 79.033319ms 12.187355ms 12.367783ms 12.627989ms 13.195701ms 13.530311ms 13.867002ms 14.120214ms 14.032195ms 15.004994ms 15.619538ms 15.843684ms 17.149219ms 15.812442ms 17.259739ms 19.019141ms 20.183859ms 22.195423ms 26.324638ms 35.802133ms google-analytics.js 12.8495ms 13.234593ms 13.257188ms 13.410396ms 13.50762ms 14.012579ms 14.513752ms 14.657808ms 14.711662ms 14.737057ms 12.6181ms 12.941449ms 13.079755ms 14.14299ms 14.719296ms 15.095659ms 15.182763ms 15.316814ms 15.87729ms 43.658657ms 88.969028ms 12.62342ms 12.672336ms 13.07322ms 13.676469ms 14.221853ms 14.228324ms 14.697255ms 14.576014ms 15.734293ms 16.583952ms 16.834214ms 18.568359ms 18.928056ms 21.178306ms 22.257264ms 21.863618ms 23.417455ms 25.794524ms 31.87216ms digitec.ch.html 25.082222ms 13.801479ms 13.793104ms 13.913294ms 14.282221ms 14.849952ms 15.559111ms 15.860264ms 16.844226ms 16.605564ms 13.386314ms 13.647842ms 13.705719ms 15.436055ms 16.84205ms 18.03647ms 18.869683ms 19.827088ms 21.304151ms 93.656512ms 263.20503ms 12.795193ms 12.8495ms 13.18075ms 14.074467ms 14.907679ms 14.729001ms 15.438699ms 15.689596ms 16.580583ms 17.515201ms 18.019094ms 19.984801ms 21.135134ms 23.917172ms 25.81584ms 29.70609ms 32.267429ms 37.950632ms 66.364592ms googletagmanager.js 25.665718ms 15.902441ms 16.30431ms 16.996436ms 17.157285ms 18.642643ms 21.581664ms 22.518252ms 23.513873ms 23.603353ms 16.3164ms 15.117379ms 15.852289ms 18.606142ms 22.424983ms 23.76822ms 26.530793ms 28.365533ms 31.775451ms 183.184175ms 416.107864ms 15.272192ms 14.950672ms 14.587691ms 15.530224ms 16.497233ms 17.318026ms 17.783193ms 18.432688ms 19.734269ms 21.291851ms 22.793436ms 25.835627ms 30.102979ms 32.741006ms 34.089953ms 41.766473ms 45.051451ms 54.074163ms 62.771881ms bootstrap.min.css 26.286002ms 13.899373ms 14.14185ms 14.481371ms 14.748387ms 15.664035ms 16.929114ms 17.791224ms 19.506138ms 19.45417ms 13.443627ms 14.294636ms 14.646113ms 16.011941ms 17.751501ms 19.324185ms 19.946413ms 21.769783ms 23.651635ms 130.488375ms 360.739976ms 12.895077ms 13.100521ms 13.504045ms 14.117929ms 15.397002ms 15.933213ms 16.169383ms 16.891482ms 17.961717ms 18.867508ms 20.235501ms 21.486164ms 23.766766ms 26.818907ms 30.022204ms 40.435403ms 44.111352ms 60.184726ms 78.983696ms bulma.min.css 61.022446ms 16.463043ms 16.551372ms 17.311686ms 17.636936ms 18.982359ms 24.037228ms 28.08923ms 41.497871ms 42.854774ms 16.614614ms 15.941518ms 16.290425ms 19.154075ms 22.676895ms 25.357839ms 27.387133ms 31.262392ms 37.984668ms 317.871011ms 930.909702ms 13.975527ms 14.236985ms 14.350435ms 16.162623ms 17.380614ms 18.336599ms 19.511693ms 20.09635ms 20.838785ms 23.510744ms 25.787559ms 27.43381ms 30.739493ms 36.232562ms 40.851237ms 74.129537ms 82.350902ms 117.358353ms 172.078676ms
For files below 10 - 15 KiB it’s fastest to not compress at all, or with zstandard level 1 - at least on a fast connection.
But bigger files perform best with low compression levels, gzip 1, brotli 1 and zstandard 1-3. On a slow connection, compression becomes more important, so higher levels perform better.
The conclusion
For files compressed ahead of time, i will use brotli on the highest settings (the default on the cli). For files compressed on the fly, i will use zstandard on level 3 (cli default is 6, nginx default is 1).
You can find all of the code in the repository on codeberg.