Comparing uglifyjs and google-closure-compiler


#1

Background

I recently shared the Elm compiler performance boosts we got from upgrading our largest application from 0.18 to 0.19 on Twitter:

In the replies, there was some discussion about output file sizes. I mentioned that I was using the google-closure-compiler, but @evancz replied saying he got better results using uglifyjs.

Evan suggested moving the discussion to Discourse:

UglifyJS vs Closure Compiler comparison

My project stats

  • 88 modules get recompiled upon removing the elm-stuff directory and running elm make
  • The project is roughly 24,535 source lines of Elm code
  • The output .js file after initial compilation is 54,259 lines

Output sizes

Below are the output sizes listed from largest to smallest. You can see that there is some disparity between uglifyjs and google-closure-compiler initially. The final result after gzip is that the two are within 1% of one another in terms of size, with GCC being approximately 500 bytes smaller than the uglifyjs output.

Method Gzip Output Size
elm make No 1728593 bytes
elm make --optimize No 1602466 bytes
elm make --optimize + uglifyjs No 386604 bytes
elm make --optimize + google-closure-compiler No 365266 bytes
elm make --optimize + uglifyjs Yes 109232 bytes
elm make --optimize + google-closure-compiler Yes 108702 bytes

uglifyjs configuration

This script is adapted from the script in Evan’s post about optimizing Elm. I have not changed the arguments supplied at all. I am using the latest version of the uglify-js package on npm (version 3.4.9).

#!/bin/sh

set -e

js="app.js"
min="app.uglify.min.js"


uglifyjs $js --compress 'pure_funcs="F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9"ure_getters,keep_fargs=false,unsafe_comps,unsafe' | uglifyjs --mangle --output=$min

echo " Initial size: $(cat $js | wc -c) bytes  ($js)"
echo "Minified size: $(cat $min | wc -c) bytes  ($min)"
echo " Gzipped size: $(cat $min | gzip -c | wc -c) bytes"

google-closure-compiler configuration

I’m using the latest version of the google-closure-compiler package on npm (version v20181210). There are no special arguments being provided.

#!/bin/sh

set -e

js="app.js"
min="app.closure.min.js"


google-closure-compiler --js=$js --js_output_file=$min

echo " Initial size: $(cat $js | wc -c) bytes  ($js)"
echo "Minified size: $(cat $min | wc -c) bytes  ($min)"
echo " Gzipped size: $(cat $min | gzip -c | wc -c) bytes"

Sharing your findings

Please feel free to share the results of your own minification findings, particularly if your results do not match up with the ones here.


#2

I was looking into optimizing with webpack the other day. And it seems like terser will replace uglifyjs in the webpack defaults.
Terser also doesn’t need be called two times, I think.

Here is where I got that from

I also added this issue to talk about improving docs https://github.com/elm/elm-lang.org/issues/797


#3

This is interesting, I never heard of google-closure-compiler before but it seems to work really well, plus it also transpiles your code, meaning one less dependency to set up!

Using elm make --optimize and your configurations exactly, I tested it with elm-spa-example, which of course is the benchmark Evan is using in his post.

UglifyJS

 Initial size:   370448 bytes  (elm.js)
Minified size:    96879 bytes  (elm.uglify.min.js)
 Gzipped size:    30597 bytes

Google Closure Compiler

 Initial size:   370448 bytes  (elm.js)
Minified size:    97254 bytes  (elm.closure.min.js)
 Gzipped size:    31100 bytes

The results are almost identical. GCC also has an option for advanced compilation, but it doesn’t seem to work with the outputted Elm code.

Considering GCC is a bit faster and will also transpile code, it seems like the way to go!


#4

Hello :sunny: I’m maintaining elm-minify which uses terser under the hood. my results for Richards spa-example look like this:

commands

git clone git@github.com:rtfeldman/elm-spa-example.git

cd elm-spa-example

elm make src/Main.elm --optimize --output=elm.js

time elm-minify elm.js

output

 Initial size:     370448 bytes
Minified size:      93144 bytes
 Gzipped size:      29838 bytes (measured using Node.js zlib)

    real time:       4939 milliseconds
    user time:       6885 milliseconds
     sys time:        116 milliseconds

I don’t know exactly why the results are better in this case, but I guess the terser-folks have done more optimizations?

  • @christian, would you mind building your project with elm-minify once, sending me the results? I’d like to see how it performs with your much bigger project! :sunny:

  • has anyone measured the runtime using uglify and/or closure?

Yup, I call terser once, and haven’t had any issues :sunny: I did some comparisons for Richard here.


#5

I tried elm-minify after elm make --optimize and here are my results:

Method Gzip Output Size
elm-minify No 360298 byes
elm-minify Yes 105151 bytes

So elm-minify beats google-closure-compiler by about 3%. However, the downside is that it also was the slowest of the three methods by a significant margin (this was executed on an AWS EC2 instance of size t2.small, which is what we use for development servers in my company):

Method Time Slowdown Factor
google-closure-compiler 6.95 seconds 1.00
uglifyjs 14.4 seconds 2.07
elm-minify 24.99 seconds 3.60