Advanced Closure Compiler vs UglifyJS2

20 January 2016   12 comments   Javascript

Powered by Fusion×

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

You can try it yourself here.

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)

1) 742371 bytes (725.0 Kb)
2) 214542 bytes (209.5 Kb) when gzip'ed
3) 21.84 seconds (0.36 minutes)

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.


Jos van den Oever
To 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.
Peter Bengtsson
Why not switch to TypeScript then?
Feels strange that a tool is a type checker and a compressor at the same time.
Ben Lesh
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.
Otto Chrons
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.
Otto Chrons
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.
Dave B
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 , 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.)
Peter Bengtsson
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.
Joe Duarte
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:
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.
Torgeir Helgevold
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:
Nuno Ferreira
It would interesting to revisit these tests again, using the native JS version of Closure:
Peter Bengtsson
Thank you for posting a comment

Your email will never ever be published

Related posts

Select all relations in PostgreSQL 10 December 2015
Best Atom packages of 2015 22 January 2016
Related by Keyword:
HTML whitespace "compression" - don't bother! 11 March 2013
Goodies from tornado-utils - part 2: tornado_static 22 September 2011
Comparing Google Closure with UglifyJS 10 July 2011
Test static resources in Django tests 02 June 2011
The awesomest way possible to serve your static stuff in Django with Nginx 24 March 2010
Related by Text:
Comparing Google Closure with UglifyJS 10 July 2011
All your images are belong to data uris 06 January 2013
Concurrent Gzip in Python 13 October 2017
HTML whitespace "compression" - don't bother! 11 March 2013
Fastest way to download a file from S3 29 March 2017