A couple of years ago I wrote a blog post titled "Comparing Google Closure with UglifyJS". It concluded that Closure Compiler compressed files down to 45.6% of the original size. And UglifyJS only 51.5%. But UglifyJS was 1220% faster so I concluded that I'm going to stick to UglifyJS.
But things have changed since 2011. UglifyJS2 came out and stealthy replaced the original implementation (npm install uglify-js
) and it has a --mangle
option. Also, in the original experimental blog post I didn't use -O advanced
when using Closure Compiler.
So I whip up a quick script to compare the two. Here's some of the output:
PATH SIZE CLOSURE (GZIP) (TIME) UGLIFY (GZIP) (TIME) starredevents.js 2060 413 240 2.63s 528 287 0.18s survey-edit.js 1672 652 362 2.67s 803 417 0.34s url-transforms.js 3396 1738 619 3.36s 2012 643 0.20s event-upload.js 4041 1510 544 2.90s 1735 593 0.20s face.js * 241355 197640 47267 16.45s 197255 46636 1.28s editor.js 1375 745 337 2.70s 842 368 0.19s details.js 1443 570 336 2.86s 867 451 0.20s autocompeter.js 996 626 268 2.75s 661 273 0.19s dashboard.js 580 296 208 3.08s 354 236 0.19s picture-add.js 708 349 237 2.71s 395 269 0.18s upload.js 4144 1592 675 3.02s 1868 737 0.21s mainmanager.js 7349 120 118 2.57s 3522 1248 0.23s edit.js 2827 1496 686 2.69s 1689 742 0.21s moment.js 99201 29190 10982 3.27s 35743 11952 0.42s durations.js 280 174 146 2.39s 192 151 0.17s RecordRTC.js 93344 30311 3925 2.72s 23066 7123 0.33s suggestions.js 318 167 148 2.59s 192 162 0.17s ... ...I ran this for 90 found files... ... TOTAL 1453771 748235 200676 277.47s 742371 214542 21.84s
I ran it like this:
$ find ~/airmozilla/airmozilla/ | grep '\.js$' |grep -v '\.min\.' | grep -v Popcorn | python compare.py
In Summary
First of all, can I just say that due to Closure Compiler being written in Java, my poor Macbook Pro's fan had to sweat and blow so hard I thought it was going to melt through the desk.
Closure Compiler:
1) 748235 bytes (730.7 Kb)
2) 200676 bytes (196.0 Kb) when gzip'ed
3) 277.47 seconds (4.6 minutes)
UglifyJS2:
1) 742371 bytes (725.0 Kb)
2) 214542 bytes (209.5 Kb) when gzip'ed
3) 21.84 seconds (0.36 minutes)
Compared:
1) Closure Compiler is 0.8% BIGGER than UglifyJS2
2) UglifyJS2 is 6.9% BIGGER than Closure Compiler when gzip'ed
3) UglifyJS2 is 12.7 times faster than Closure Compiler
Also, worth noting that in 2 files Closure Compiler failed with -O advanced
so those 2 files had to be re-attempted with -O simple
.
In Conclusion
It's rare that the speed matters all that much because it's usually done in a build step that is done once only on deployment (or something such) but having to mess around with Java is really not worth it when Node is so pervasive and almost always available.
UglifyJS2 is my preferred choice.
Comments
Post your own commentTo me the point of closure compiler is that it does a lot of sanity checks. So JS can be developed and tested uncompiled and a precommit hook or CI test can check that the code has 100% type annotation and no compile errors. Such checks make it possible to work on a large codebase with a secure feeling and with less need for simple tests.
Why not switch to TypeScript then?
Feels strange that a tool is a type checker and a compressor at the same time.
A fair point, but Closure compiler will use some of the comment annotations like `@private` to make decisions about what names to mangle. While it's at it, it can assert types, too, I suppose.
GCC has slow start-up (being Java), so it suffers from running it individually on a lot of small files. A more realistic scenario would be to compare the two when run on a bundled result.
For example running both on a 3.8MB JS file produced by the Scala.js compiler the results are:
GCC 931k 24s (220k GZ)
Uglify 1943k 9s (318k GZ)
Most of the size difference comes from the fact that Uglify doesn't do as good a job at name mangling as GCC does, on a global level.
Furthermore if you run GCC in a warmed up JVM (such as a service, or within a build tool like SBT), then the times go radically down. For example within SBT the first run of GCC takes 16.5s, but then it drops to 11-12s. Same file run through Uglify takes 7.1s to process. So the differences are not that big anymore, especially considering that the final JS is 100% larger with Uglify :)
Unfortunately GCC does not offer a daemon/service mode (except via their web API), so using GCC in an optimal way in a JS project is not that easy.
Perhaps I am missing the point.
I was under the impression that the primary reasons one would use the Closure Compiler is for:
1) Catching errors in your javascript code.
2) Mangling your code to protect your intellectual property.
3) Removing unused code from your download.
For item 1, we have switched to TypeScript and do not benefit from the closure compiler. But if we were writing in ES5, I could certainly see the benefit.
For Item 2, if you don't mind if all your exported functions and properties are not mangled when even used between libraries (i.e. not from a web page), UglifyJS does a fine job in my limited testing. If you want those exported functions and properites mangled also so that only functions and properties expected to be accessed from a web page (extern) are unmangled, then I am under the impression that UglifyJS does not handle this case. But I may not have looked close enough because Item 3 is the deal breaker for me unless I am missing something.
I have only performed limited tests with UglifyJS and may just lack the knowledge of how to use the tool effectively. But after reading https://github.com/mishoo/UglifyJS2/issues/743 , the following is the conclusion I came to. Is my conclusion incorrect?
For Item 3, I don't understand how your test did anything with testing code removal, which is the main reason I would be using this tool. Presumably you have left the libraries in tact and not removed any "new" unused code other than what UglifyJS and Closure Compiler can remove in the simplest cases (i.e. unreachable code). It is my intent to create my own libraries and use third party libraries and on any given project, have the tooling figure out what it can remove with the minimal amount of guidance from me. Some annotating of my source code and adding a few special function calls to allow this seems like time spent well to remove substantial parts of libraries for a deployment. In other words, I want to have a function I call in some applications and not in others and I want my tooling to remove the function when it is not required.
I am trying to get back into Javascript and my last production applications were pre node js, modules, minification, .... So I may be coming from a completely ignorant position. Any assistance you can provide so that I understand what I am missing either from your article or in my assumptions would be appreciated.
I am currently reevaluating our tooling. Our current tooling is:
1) TypeScript with internal modules (namespaces)
2) Some home grown merging, extern generation, and fixing files to preserve maps
3) Running the closure compiler in production.
I am evaluating something like:
1) TypeScript (external modules)
2) JSPM Dev
3) JSPM Bundle / UglifyJS?
4) Possibly using Closure Advanced on the final bundle (not there yet and don't know if it is even possible.)
I do not doubt that Closure Compiler has some better features when it comes to dropping things that aren't used. It just takes an age longer if that's not a key priority.
Note that in the issue you linked, the guy completely misunderstood how global scope works in JavaScript. I don't know why they don't just close the issue. UglifyJS can remove unused functions. As long has they're not defined on a global scope level.
Hmm... I wonder if that option was or wasn't used in this blog post.
Nice test! We need more of this.
I'd love to see the execution time and other end-user performance metrics for the compiled code. That's what matters most in the end, not how long it takes a developer to minify his code. Closure is supposed to do some deep optimization, so I'd like to see how it plays out.
I also suggest putting the same variable columns next to each other so it's easier to compare Closure and UglifyJS, e.g. on size. Right now, I have to scan across several columns to compare the two. I'd also check out Bloomberg's bucklescript if there's some way to make it a JS-to-JS transpiler. I just learned about it: https://github.com/bloomberg/bucklescript/wiki/Why-bucklescript-matters-for-Javascript-platform
You missed the point with this comparison because Closure Compiler is an advanced optimiser. Uglify is only a compressor but CC is far beyond that so it's obviously going to be much slower.
I have not evaluated uglify 2 yet, but I see it has a few settings that might allow for aggressive optimizations. Did some POC work with Closure Compiler though. The results are pretty amazing when using the ADVANCED option, but it requires you to design your code with CC in mind. Traditionally I've just used uglify for conservative (safe) minification.
Here are my Closure Compiler findings if you are interested: http://www.syntaxsuccess.com/viewarticle/using-the-closure-compiler---advanced_optimizations
It would interesting to revisit these tests again, using the native JS version of Closure: https://github.com/google/closure-compiler-js
Good idea! See if you can fork https://gist.github.com/peterbe/cf049e4c0c2dcb47238d