Rich-Harris / butternut

The fast, future-friendly minifier
https://butternut.now.sh
MIT License
1.17k stars 17 forks source link

Closure Compiler (Java) compresses better, a lot better. #35

Open leeoniya opened 7 years ago

leeoniya commented 7 years ago

The compression is better than Babili and Closure Compiler

Unfortunately this claim is factually not true today, not by a long shot in fact:

https://github.com/leeoniya/domvm/blob/2.x-dev/dist/dev/domvm.dev.min.js

GCC yields 15.6 KB. butternut comes in at 16.7 KB.

The good news is that it passes ~97% of the written tests after compilation and compresses impressively fast. I haven't checked how the final build performs in benchmarks, but GCC does a good amount optimizations that result in faster perf vs Uglify.

Rich-Harris commented 7 years ago

It depends on the codebase. GCC performs better on some — domvm apparently being one of them — but not others. I'll re-run the numbers in a bit and post them

leeoniya commented 7 years ago

Uglify's at 16.4 KB. i wonder why GCC does so much better :/

EDIT: just to be clear, i'd prefer to move to a Rich Harris tooling monoculture 😆

Rich-Harris commented 7 years ago

Alright, I ran some numbers — minified, zipped, and times. (Don't pay too much attention to the times, I'm half way through figuring out a better way to benchmark this stuff — it doesn't seem to make sense to minify each file multiple times so that functions warm up when minifiers typically run cold.)

You can see that it really does depend on the codebase. For every domvm or Moment, there's a Backbone — 8.29kb zipped in Closure, 7.47kb in Uglify and 7.57 in Butternut — or a jQuery/Immutable/Ramda/React, etc. Need to chart these numbers but I would say that Closure is generally either the largest output or the second largest behind Babili. I think domvm is perhaps an unusual case (note that I'm testing the closure-compiler-js, not the Java version — will edit the README to make that clearer).

It looks like Closure is often injecting some polyfills ($jscomp) which are probably unnecessary. It's possible that I'm doing something wrong, but I tried fiddling with e.g. the languageOut option and all I get are cryptic errors.

Anyway, it's early days — there's no reason in principle that Butternut can't adopt the same optimisations that Uglify and Closure have perfected, but there are reasons that Uglify, Closure and Babili will never be as fast as Butternut 😉

acorn.js (132 kB): ✓ babili : 74.2 kB / 21.6 kB in 2.4s ✓ butternut : 72.3 kB / 21 kB in 244ms ✓ closure : 72.8 kB / 21.2 kB in 7.4s ✓ uglify : 65.9 kB / 21.5 kB in 1.1s ✓ uglify-es : 65.9 kB / 21.5 kB in 1.3s async.js (183 kB): ✓ babili : 25.5 kB / 8.98 kB in 1.4s ✓ butternut : 24.2 kB / 8.64 kB in 187ms ✓ closure : 24.6 kB / 8.72 kB in 4.3s ✓ uglify : 23.3 kB / 8.17 kB in 813ms ✓ uglify-es : 23.3 kB / 8.17 kB in 803ms backbone.js (72.2 kB): ✓ babili : 22.7 kB / 7.73 kB in 924ms ✓ butternut : 22.7 kB / 7.57 kB in 104ms ✓ closure : 24.8 kB / 8.29 kB in 2.7s ✓ uglify : 22.4 kB / 7.44 kB in 347ms ✓ uglify-es : 22.4 kB / 7.44 kB in 459ms d3.js (459 kB): ✓ babili : 240 kB / 78.7 kB in 19s ✓ butternut : 220 kB / 73.7 kB in 1.2s ✓ closure : 218 kB / 75.7 kB in 21.6s ✓ uglify : 216 kB / 73 kB in 4.4s ✓ uglify-es : 216 kB / 73 kB in 5.8s handlebars.js (167 kB): ✓ babili : 74.1 kB / 22.5 kB in 2.5s ✓ butternut : 76.1 kB / 22.3 kB in 228ms ✓ closure : 75.9 kB / 22.7 kB in 5s ✓ uglify : 73.1 kB / 21.7 kB in 1.1s ✓ uglify-es : 73.1 kB / 21.7 kB in 1.5s immutable.js (142 kB): ✓ babili : 61.7 kB / 16.6 kB in 2.2s ✓ butternut : 58.4 kB / 15.9 kB in 198ms ✓ closure : 60.9 kB / 17.3 kB in 4.4s ✓ uglify : 56.6 kB / 15.6 kB in 840ms ✓ uglify-es : 56.6 kB / 15.6 kB in 1.1s jquery.js (268 kB): ✓ babili : 96 kB / 32.9 kB in 4.7s ✓ butternut : 89 kB / 30.6 kB in 280ms ✓ closure : 89.4 kB / 31.8 kB in 6s ✓ uglify : 86.8 kB / 30.4 kB in 1.4s ✓ uglify-es : 86.8 kB / 30.4 kB in 1.8s lodash.js (540 kB): ✓ babili : 77.9 kB / 26 kB in 4.9s ✓ butternut : 73.9 kB / 25.8 kB in 399ms ✓ closure : 74.6 kB / 25.3 kB in 7.3s ✓ uglify : 71.2 kB / 25.2 kB in 1.6s ✓ uglify-es : 71.2 kB / 25.2 kB in 2.1s marked.js (28.7 kB): ✓ babili : 16.2 kB / 5.22 kB in 574ms ✓ butternut : 16.4 kB / 5.24 kB in 36ms ✓ closure : 16.3 kB / 5.3 kB in 2s ✓ uglify : 16 kB / 5.18 kB in 195ms ✓ uglify-es : 16 kB / 5.18 kB in 245ms moment.js (129 kB): ✓ babili : 54.3 kB / 17.2 kB in 2.3s ✓ butternut : 53.1 kB / 17.4 kB in 148ms ✓ closure : 49.8 kB / 16.5 kB in 3.6s ✓ uglify : 51.4 kB / 16.8 kB in 783ms ✓ uglify-es : 51.4 kB / 16.8 kB in 992ms preact.js (20.5 kB): ✓ babili : 8.41 kB / 3.47 kB in 353ms ✓ butternut : 8.15 kB / 3.4 kB in 28ms ✓ closure : 7.89 kB / 3.35 kB in 2.2s ✓ uglify : 8.07 kB / 3.35 kB in 145ms ✓ uglify-es : 8.07 kB / 3.35 kB in 188ms ramda.js (307 kB): ✓ babili : 47.1 kB / 12 kB in 1.6s ✓ butternut : 44.5 kB / 11.8 kB in 277ms ✓ closure : 45.5 kB / 12.9 kB in 3.7s ✓ uglify : 42.8 kB / 11.4 kB in 2.8s ✓ uglify-es : 42.8 kB / 11.4 kB in 3.6s react-dom.js (646 kB): ✓ babili : 195 kB / 61.1 kB in 5.2s ✓ butternut : 198 kB / 59.1 kB in 506ms ✓ closure : 190 kB / 58.6 kB in 9.7s ✓ uglify : 191 kB / 57.7 kB in 2.4s ✓ uglify-es : 191 kB / 57.7 kB in 3.1s react.js (136 kB): ✓ babili : 39.2 kB / 13.5 kB in 1.2s ✓ butternut : 40.2 kB / 13.2 kB in 111ms ✓ closure : 45.6 kB / 14.9 kB in 3.3s ✓ uglify : 38.6 kB / 12.7 kB in 508ms ✓ uglify-es : 38.6 kB / 12.7 kB in 639ms redux.js (30.8 kB): ✓ babili : 8.5 kB / 3.29 kB in 209ms ✓ butternut : 8.76 kB / 3.23 kB in 30ms ✓ closure : 10.7 kB / 3.8 kB in 1.7s ✓ uglify : 8.3 kB / 3.05 kB in 106ms ✓ uglify-es : 8.3 kB / 3.05 kB in 148ms Rx.js (582 kB): ✓ babili : 150 kB / 30.5 kB in 6.9s ✓ butternut : 143 kB / 29.8 kB in 445ms ✓ closure : 141 kB / 29.8 kB in 7.6s ✓ uglify : 141 kB / 28.9 kB in 1.9s ✓ uglify-es : 141 kB / 28.9 kB in 2.5s three.js (1.03 MB): ✓ babili : 540 kB / 134 kB in 14.9s ✓ butternut : 519 kB / 130 kB in 1.1s ✓ closure : 513 kB / 129 kB in 1m 9.1s ✓ uglify : 509 kB / 128 kB in 5.9s ✓ uglify-es : 509 kB / 128 kB in 7.1s underscore.js (52.9 kB): ✓ babili : 16.9 kB / 5.96 kB in 590ms ✓ butternut : 16.6 kB / 5.76 kB in 134ms ✓ closure : 18.9 kB / 6.56 kB in 2.4s ✓ uglify : 16.2 kB / 5.68 kB in 264ms ✓ uglify-es : 16.2 kB / 5.68 kB in 353ms vue.js (253 kB): ✓ babili : 104 kB / 37.3 kB in 5.2s ✓ butternut : 98.4 kB / 36.1 kB in 320ms ✓ closure : 96.4 kB / 36.1 kB in 7.5s ✓ uglify : 95.3 kB / 35.5 kB in 1.5s ✓ uglify-es : 95.3 kB / 35.5 kB in 1.9s
leeoniya commented 7 years ago

the speed of closure-js is terrible. the way i got rid of $jscomp in the Java version was by setting ECMASCRIPT6_STRICT in and ECMASCRIPT5_STRICT out [1].

[1] https://github.com/leeoniya/domvm/blob/2.x-dev/build.js#L142

Rich-Harris commented 7 years ago

I think that must be a Java-only option, it doesn't seem to have any effect on the JS output. Perhaps we should try and benchmark the Java version as well in the interests of complete fairness.

leeoniya commented 7 years ago

https://raw.githubusercontent.com/mrdoob/three.js/dev/build/three.js

java -jar compiler.jar --language_in=ECMASCRIPT6_STRICT --language_out=ECMASCRIPT5_STRICT --js three.js --js_output_file three.min.js

java -jar compiler.jar --language_in=ECMASCRIPT5_STRICT --language_out=ECMASCRIPT5_STRICT --js three.js --js_output_file three.min.js

produces 491k 495k (with $jscomp, couldnt get rid of them with this codebase). runs in 4-5s on my rather modest Thinkpad T440S; none of this 1m 9.1s nonsense.

specs

when the JS port of GCC was announced [1], my first impulse was to test it out [2] cause i'm one of those people who hates "compile everything to js just because we can now". 3-4x slowdown seems to have been a rather generous assessment. looks like the slowdown is O(n^2) with respect to code size.

[1] https://news.ycombinator.com/item?id=13116526 [2] https://news.ycombinator.com/item?id=13117165

Rich-Harris commented 7 years ago

Wow. I'd assumed that since closure-compiler-js was compiled from the Java source, it would yield the same results (compression-wise, not perf-wise). Needs a 'don't actually use this!' notice on the repo.

Have updated the README to make it clear I'm talking about the -js version.

jbmonroe commented 7 years ago

You guys are killing me. GCC is the GNU C++ compiler. I'm thinking "what? The c++ compiler compresses JavaScript?"

Rich-Harris commented 7 years ago

Ha, yep — GNU C++ Compiler !== Google Closure Compiler 😆

leeoniya commented 7 years ago

yeah sry, i started seeing that abbr used recently and it threw me off too, but Github doesnt have autocomplete and who has the time to write it all out (what are we, C++ devs :D)?