ESnext Performance
Why bother?
Benedikt Meurer
Proprietary
Proprietary
Benedikt Meurer
Proprietary
ESnext = ES2015+
Designer
Developer
Implementor
Proprietary
Source: stateofjs.com
Proprietary
“[...] large proportion of developers have already jumped on the [ES6] bandwagon, and almost all of those who haven’t yet want to learn it [...]“
”[...] in terms of satisfaction the big winner here is ES6, and [...] it’s now the default way to write JavaScript”
Source: stateofjs.com
Proprietary
Proprietary
Proprietary
Proprietary
ESnext today
...
Proprietary
ESnext tomorrow
+
Proprietary
ES2015 support is strong
Proprietary
ES2015 support is strong
Proprietary
ESnext - Two worlds
Native ES2015+ code
Generated ES3/ES5 code
Proprietary
function sum(a) {� let s = 0;� for (let x of a) s += x;� return s;�}
function sum(a) {� var s = 0;� var _iteratorNormalCompletion = true;� var _didIteratorError = false;� var _iteratorError = undefined;� try {� for (var _iterator = a[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {� var x = _step.value;� s += x;� }� } catch (err) {� _didIteratorError = true;� _iteratorError = err;� } finally {� try {� if (!_iteratorNormalCompletion && _iterator.return) {� _iterator.return();� }� } finally {� if (_didIteratorError) {� throw _iteratorError;� }� }� }� return s;�}
Proprietary
function sum(a) {� let s = 0;� for (let x of a) s += x;� return s;�}
function sum(a) {� var s = 0;� var _iteratorNormalCompletion = true;� var _didIteratorError = false;� var _iteratorError = undefined;� try {� for (var _iterator = a[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {� var x = _step.value;� s += x;� }� } catch (err) {� _didIteratorError = true;� _iteratorError = err;� } finally {� try {� if (!_iteratorNormalCompletion && _iterator.return) {� _iterator.return();� }� } finally {� if (_didIteratorError) {� throw _iteratorError;� }� }� }� return s;�}
Proprietary
function sum(a) {� let s = 0;� for (let x of a) s += x;� return s;�}
function sum(a) {� var s = 0;� var _iteratorNormalCompletion = true;� var _didIteratorError = false;� var _iteratorError = undefined;� try {� for (var _iterator = a[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {� var x = _step.value;� s += x;� }� } catch (err) {� _didIteratorError = true;� _iteratorError = err;� } finally {� try {� if (!_iteratorNormalCompletion && _iterator.return) {� _iterator.return();� }� } finally {� if (_didIteratorError) {� throw _iteratorError;� }� }� }� return s;�}
71 chars
651 chars
Proprietary
function sum(a) {� let s = 0;� for (let x of a) s += x;� return s;�}
function sum(a) {� var s = 0;� for (var _iterator = a, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {� var _ref;� if (_isArray) {� if (_i >= _iterator.length) break;� _ref = _iterator[_i++];� } else {� _i = _iterator.next();� if (_i.done) break;� _ref = _i.value;� }� var x = _ref;� s += x;� }
return s;�}
es2015-loose
Proprietary
function sum(a) {� let s = 0;� for (let x of a) s += x;� return s;�}
function sum(a) {� var s = 0;� for (var _iterator = a, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {� var _ref;� if (_isArray) {� if (_i >= _iterator.length) break;� _ref = _iterator[_i++];� } else {� _i = _iterator.next();� if (_i.done) break;� _ref = _i.value;� }� var x = _ref;� s += x;� }
return s;�}
es2015-loose
71 chars
421 chars
Proprietary
async function all(c, a) {� for (const f of c) a = await f(a);� return a;�}
var all=function(){var b=_asyncToGenerator(regeneratorRuntime.mark(function d(e,g){var h,i,j,k,l,m;return regeneratorRuntime.wrap(function(o){for(;;)switch(o.prev=o.next){case 0:h=!0,i=!1,j=void 0,o.prev=3,k=e[Symbol.iterator]();case 5:if(h=(l=k.next()).done){o.next=13;break}return m=l.value,o.next=9,m(g);case 9:g=o.sent;case 10:h=!0,o.next=5;break;case 13:o.next=19;break;case 15:o.prev=15,o.t0=o["catch"](3),i=!0,j=o.t0;case 19:o.prev=19,o.prev=20,!h&&k.return&&k.return();case 22:if(o.prev=22,!i){o.next=25;break}throw j;case 25:return o.finish(22);case 26:return o.finish(19);case 27:return o.abrupt("return",g);case 28:case"end":return o.stop();}},d,this,[[3,15,19,27],[20,,22,26]])}));return function(){return b.apply(this,arguments)}}();�
+
function _asyncToGenerator(b){return function(){var d=b.apply(this,arguments);return new Promise(function(e,g){function h(i,j){try{var k=d[i](j),l=k.value}catch(m){return void g(m)}return k.done?void e(l):Promise.resolve(l).then(function(m){h("next",m)},function(m){h("throw",m)})}return h("next")})}}
+
Proprietary
async function all(c, a) {� for (const f of c) a = await f(a);� return a;�}
var all=function(){var b=_asyncToGenerator(regeneratorRuntime.mark(function d(e,g){var h,i,j,k,l,m;return regeneratorRuntime.wrap(function(o){for(;;)switch(o.prev=o.next){case 0:h=!0,i=!1,j=void 0,o.prev=3,k=e[Symbol.iterator]();case 5:if(h=(l=k.next()).done){o.next=13;break}return m=l.value,o.next=9,m(g);case 9:g=o.sent;case 10:h=!0,o.next=5;break;case 13:o.next=19;break;case 15:o.prev=15,o.t0=o["catch"](3),i=!0,j=o.t0;case 19:o.prev=19,o.prev=20,!h&&k.return&&k.return();case 22:if(o.prev=22,!i){o.next=25;break}throw j;case 25:return o.finish(22);case 26:return o.finish(19);case 27:return o.abrupt("return",g);case 28:case"end":return o.stop();}},d,this,[[3,15,19,27],[20,,22,26]])}));return function(){return b.apply(this,arguments)}}();�
+
function _asyncToGenerator(b){return function(){var d=b.apply(this,arguments);return new Promise(function(e,g){function h(i,j){try{var k=d[i](j),l=k.value}catch(m){return void g(m)}return k.done?void e(l):Promise.resolve(l).then(function(m){h("next",m)},function(m){h("throw",m)})}return h("next")})}}
+
57 chars
720 chars
289 chars
2.1KiB
17.8KiB
Proprietary
JavaScript Transfer Size
426kB
of JavaScript on average!
Proprietary
Proprietary
15-30%*
Transpilation overhead!
* estimate based on my experiments
Proprietary
...not just transfer cost
Proprietary
...not just transfer cost
Proprietary
JavaScript engines optimize patterns
Proprietary
Proprietary
Traditional patterns
Proprietary
Fast array iteration
Proprietary
function sum(a) {� let s = 0;� for (let x of a) s += x;� return s;�}
function sum(a) {� var s = 0;� var _iteratorNormalCompletion = true;� var _didIteratorError = false;� var _iteratorError = undefined;� try {� for (var _iterator = a[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {� var x = _step.value;� s += x;� }� } catch (err) {� _didIteratorError = true;� _iteratorError = err;� } finally {� try {� if (!_iteratorNormalCompletion && _iterator.return) {� _iterator.return();� }� } finally {� if (_didIteratorError) {� throw _iteratorError;� }� }� }� return s;�}
Proprietary
function sum(a) {� let s = 0;� for (let x of a) s += x;� return s;�}
function sum(a) {� var s = 0;� var _iteratorNormalCompletion = true;� var _didIteratorError = false;� var _iteratorError = undefined;� try {� for (var _iterator = a[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {� var x = _step.value;� s += x;� }� } catch (err) {� _didIteratorError = true;� _iteratorError = err;� } finally {� try {� if (!_iteratorNormalCompletion && _iterator.return) {� _iterator.return();� }� } finally {� if (_didIteratorError) {� throw _iteratorError;� }� }� }� return s;�}
Proprietary
for (var _iterator = a[Symbol.iterator](), _step;
!(_step = _iterator.next()).done; ) {� var x = _step.value;� s += x;�}
Array.prototype[Symbol.iterator]
%ArrayIteratorPrototype%.next
ToBoolean(Get(IterResultObject::done))
Get(IterResultObject::value)
🎉 Fast array iteration! 🎉
Proprietary
Compared to naive ES5
function sum(a) {� let s = 0;� for (let x of a) s += x;� return s;�}
function sum(a) {� var s = 0;� for (var i = 0; i < a.length; ++i) s += a[i];
return s;�}
Proprietary
Performance cliffs
Proprietary
Proprietary
Proprietary
Proprietary
Proprietary
Proprietary
Symbol.hasInstance vs instanceof 😞
fast
slow
Proprietary
Symbol.hasInstance vs instanceof 😄
Proprietary
Surprising footgun
console.time('replace');�var str = 'abc';�for (var i = 0; i < 1e7; ++i) {
str = str.replace('a', 'b');
}�console.timeEnd('replace');
import "babel-polyfill";
console.time('replace');�var str = 'abc';�for (var i = 0; i < 1e7; ++i) {
str = str.replace('a', 'b');
}�console.timeEnd('replace');
vs.
818ms
175ms
Proprietary
Simplified footgun
�console.time('replace');�var str = 'abc';�for (var i = 0; i < 1e7; ++i) {� str = str.replace('a', 'b');�}�console.timeEnd('replace');
String.prototype.unused = () => 0;��console.time('replace');�var str = 'abc';�for (var i = 0; i < 1e7; ++i) {� str = str.replace('a', 'b');�}�console.timeEnd('replace');
vs.
791ms
175ms
Proprietary
Symbol.replace vs String.prototype.replace
slow
Proprietary
Proprietary
Fast ES2015+
Proprietary
Proprietary
Proprietary
Slowdown ES2015+ vs ES5 (Six-Speed)
Proprietary
Driving ESnext performance further
Proprietary
WYWIWYS
“What you write is what you ship”
A.K.A. “transpile less”
Proprietary
babel-preset-env
npm install babel-preset-env --save-dev
“A Babel preset that can automatically determine the Babel plugins and polyfills you need based on your supported environments.”
Proprietary
Proprietary
tsc --target ES2015 …
tsc --target ES2016 …
tsc --target ES2017 …
tsc --target ESNext …
Proprietary
Migrate to ES2015 via modules
Every browser that supports modules supports ES2015 (> 95%)
<script nomodule src="old.js"></script>�<script type="module" src="new.js"></script>
Proprietary
What’s next?
Proprietary
Takeaways
Proprietary
The End
Proprietary
Proprietary