How much does ECMAScript 2015 cost?

Ben Newman
Meteor Blog
Published in
4 min readAug 5, 2015

--

In case you missed it, Meteor 1.2 will enable support for ES2015+ by default.

I dove into the gory details of how it all works in my recent Devshop talk, but if you watch all the way to the end, and then keep listening to the Q&A, you’ll see I didn’t really have a satisfactory answer for the question about the cost of the ecmascript package, in terms of runtime library size, generated code size, and increased restart times of the development server.

The same question got posted on the forums, and I took some time to answer it in depth. What you’re reading right now is essentially a repost of that answer, with this introduction added for context.

The tl;dr is that the ecmascript package is quite lightweight. First I'll explain the design decisions that allow it to cost so little, and then I'll give some data about client bundle sizes with and without the ecmascript and es5-shim packages.

Design

Optionality

Both packages (ecmascript and es5-shim) will be removable, for those who do not wish to enable the new features, or who do not care about supporting older browsers. This optionality is an important design constraint for the ES2015 project, precisely so that no one can complain we made their app slower or larger without giving them the opportunity to opt out. I don't mean to suggest we actually think the new features are too slow or too bloated for production use. In fact, if you let those vague concerns keep you from using the new ecmascript package, you'll just be cheating yourself out of a better development experience!

Caching

All compilation is cached, so the cost of incremental reloads is never more than the cost of compiling the files that changed, and only those files. If recompilation ever takes longer than switching from your text editor to your browser, something has gone terribly wrong, and you should file a GitHub issue (with a reproduction, if possible).

If you are writing a package that uses ecmascript but also contains large files that should not be compiled, you can selectively disable compilation when you call api.addFiles by passing the {transpile:false}option, e.g.

api.addFiles("backbone.js", "client", { transpile: false });

Runtime footprint

Here is the entire runtime library that the ecmascript package uses. It contains only the helper functions we need, so it's smaller than Babel's own library of external helpers. The benefit of using a runtime library is that we don't have to keep injecting the same helper functions at the top of every compiled file, which is what Babel does by default.

Generated code size

As part of our due diligence in deciding which language features to enable, we examined the output of every candidate transformation.

In some cases we decided not to enable the transformation, either because its runtime was large, or because the generated code was verbose. Generator functions are a good example of both problems, and I should know, because I am the author of Regenerator. That said, if you have a good use case for generator functions in Meteor code, let us know, and we will consider enabling them.

In other cases we tweaked the Babel options so that the generated code would be smaller or faster. For example, we compile for-of loops in "loose" mode, so that code like this

for (let x of foo()) { 
console.log(x);
}

gets translated to this instead of this.

In other words, if you refer to this performance comparison table, make sure you look at the babel-loose rows instead of the babel rows. For example, for-of loops over arrays are much faster in loose mode.

Pay for what you use

Perhaps most importantly, any code that avoids using ES2015 features will have exactly the same generated code size and performance characteristics as ordinary ES5 code, because the compiler will just leave it untouched.

Since truly performance critical code tends to be isolated in relatively few hot spots, you always have the option of rewriting just those few bits of code in whatever style performs best, and otherwise reaping the benefits of the new language features throughout the overwhelming majority of your code.

When native support eventually catches up, your code will be ready to take full advantage of the native performance boost — but only if you’ve already been using the new language features via compilation!

Avoid relying on microbenchmarks

When and where performance optimization will be necessary is highly dependent on the details of your specific application. I’m reluctant to talk about performance without referring to real-world examples, because microbenchmarks are misleading. Without actually looking at someone’s code, it’s difficult to give any meaningful advice about performance optimization, and so I would discourage anyone from making unspecific claims (positive or negative!) about the overhead of the ecmascript package.

Let’s have some numbers, then!

Using the latest devel branch version of Meteor, I created a new app, then built it for production with all four combinations of enabling/disabling the ecmascript and es5-shim packages.

Listed below are the resulting sizes of the bundle/programs/web.browser/<hash>.js file (i.e. the combined .js bundle that gets sent to the browser in production):

  • Without ecmascript, without es5-shim: 327.80KB (+0KB, 1x)
  • With ecmascript, without es5-shim: 335.95KB (+8.15KB, 1.025x)
  • Without ecmascript, with es5-shim: 342.40KB (+14.60KB, 1.045x)
  • With ecmascript, with es5-shim: 350.55KB (+22.75KB, 1.069x)

If you use Gzip compression (and you do, unless you use meteor run in production, in which case: ಠ_ಠ), the difference between the largest and smallest versions is only 7.66KB (or just 2.63KB with ecmascript only).

Is that enough to cause concern? We can’t make that call for you, but now you have enough information to decide for yourself.

--

--

JavaScript whisperer, scruffy dog guardian, recovering sarcast, aspiring ironist. Meebo, Apture, Mozilla, Quora, Facebook, and Meteor have employed me.