{"id":783,"date":"2016-07-05T11:45:49","date_gmt":"2016-07-05T11:45:49","guid":{"rendered":"http:\/\/blog.mozilla.org\/javascript\/?p=783"},"modified":"2017-12-21T13:47:25","modified_gmt":"2017-12-21T13:47:25","slug":"ionmonkey-evil-on-your-behalf","status":"publish","type":"post","link":"https:\/\/blog.mozilla.org\/javascript\/2016\/07\/05\/ionmonkey-evil-on-your-behalf\/","title":{"rendered":"IonMonkey: Evil on your behalf"},"content":{"rendered":"<blockquote>\n<div id=\"magicdomid6\" class=\"\"><span class=\"\">\u201cProgrammers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered.\u201d<\/span><\/div>\n<div id=\"magicdomid9\" class=\"\"><span class=\"\">\u2014 <a href=\"https:\/\/en.wikiquote.org\/wiki\/Donald_Knuth\">Donald Knuth<\/a><\/span><span class=\"author-a-2z73zcz74zraz83zz88zez68zhz77zluz86zz76z\"><br \/>\n<\/span><\/div>\n<\/blockquote>\n<div id=\"magicdomid13\" class=\"\"><span class=\"\">This quote is not as well known as the end of it<\/span><span class=\"author-a-2z73zcz74zraz83zz88zez68zhz77zluz86zz76z\">, often shortened to \u201c<em>premature optimization is the root of all <a href=\"https:\/\/blog.mozilla.org\/javascript\/2016\/07\/05\/ionmonkey-evil-on-your-behalf\/\">evil<\/a><\/em>\u201d.<\/span> <span class=\"author-a-2z73zcz74zraz83zz88zez68zhz77zluz86zz76z\">I<\/span><span class=\"\">t highlights why you should prefer code which is more maintainable, as long as the performance of it does not <\/span><span class=\"author-a-2z73zcz74zraz83zz88zez68zhz77zluz86zz76z\">impact the user.<\/span><\/div>\n<div class=\"\">&nbsp;<\/div>\n<p><span class=\"\">Maintainable code is a matter of taste. For example, we often teach students to dislike \u201cgoto\u201d in C, but the best <a href=\"http:\/\/kernel-janitor.sourceforge.net\/kernel-janitor\/docs\/driver-howto.html#3.1\">error handling code<\/a> I <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">have seen (<\/span><span class=\"\">in C) comes from the Linux kernel, and is using \u201cgoto\u201d&#8217;s. Maintainable code in JavaScript is also a matter of taste<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">. S<\/span><span class=\"\">ome might prefer <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">using <\/span><span class=\"\">ES6 features, <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">while others <\/span><span class=\"\">might prefer functional programing approach with lambdas or even us<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">ing<\/span><span class=\"\"> some framework.<\/span><\/p>\n<div id=\"magicdomid29\" class=\"\"><span class=\"\">In most cases, we add more abstractions, we add more memory, and more code<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">, t<\/span><span class=\"\">hus giving even more reasons <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">for the code to be slower<\/span><span class=\"\">. Today, I will introduce <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">two <\/span><span class=\"\">optimizations <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">that<\/span><span class=\"\"> made it into IonMonkey, which are known <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">as <\/span><span class=\"\">Scalar Replacement and Branch Pruning.<\/span><\/div>\n<div id=\"magicdomid30\" class=\"\">&nbsp;<\/div>\n<h2 id=\"magicdomid31\"><span class=\"\">Scalar Replacement<\/span><\/h2>\n<div id=\"magicdomid32\" class=\"\">&nbsp;<\/div>\n<div id=\"magicdomid33\" class=\"\"><span class=\"author-a-2z73zcz74zraz83zz88zez68zhz77zluz86zz76z\">The goal of Scalar Replacement is to reduce the amount of memory needed to represent objects in memory, by replacing object properties with local variables.\u00a0 Then, when all objects properties are replaced, we can remove the object and avoid its memory overhead (i-e allocation, accesses, and GCs).<\/span><\/div>\n<div id=\"magicdomid34\" class=\"\">&nbsp;<\/div>\n<div id=\"magicdomid35\" class=\"\"><span class=\"author-a-2z73zcz74zraz83zz88zez68zhz77zluz86zz76z\">For example, in the following code, we have a few functions for manipulating complex numbers as a tuple of a real and an imaginary part.\u00a0 When compiling the <code>norm_multiply<\/code> function, the <\/span><span class=\"author-a-z82zpbz89za0kndz84zz78zgdz83zz83zz74z\">I<\/span><span class=\"author-a-2z73zcz74zraz83zz88zez68zhz77zluz86zz76z\">nlining phase will bake the code of <code>complex<\/code> and <code>norm<\/code> functions inside the <code>norm_multiply<\/code> function.<\/span><\/div>\n<div id=\"magicdomid37\" class=\"\">\n<pre><code class=\"js\">\r\nfunction complex(r, i) {\r\n    return { r: r, i: i };\r\n}\r\nfunction norm(c) {\r\n    return Math.sqrt(c.r * c.r + c.i * c.i);\r\n}\r\nfunction norm_multiply(a, b) {\r\n    var mul = complex(a.r * b.r - a.i * b.i, a.r * b.i + a.i * b.r);\r\n    return norm(mul);\r\n}<\/code><\/pre>\n<div id=\"magicdomid50\" class=\"\"><span class=\"author-a-2z73zcz74zraz83zz88zez68zhz77zluz86zz76z\">Thus, Scalar Replacement handles the object coming from the <code>complex<\/code> function, and replaces it by two local variables.\u00a0 The object is no longer needed and IonMonkey effectively runs the following code:<br \/>\n<\/span><\/div>\n<pre><code class=\"js\">\r\nfunction norm_multiply(a, b) {\r\n\u00a0\u00a0\u00a0 var mul_r = a.r * b.r - a.i * b.i;\r\n\u00a0\u00a0\u00a0 var mul_i = a.r * b.i + a.i * b.r;\r\n\u00a0\u00a0\u00a0 return Math.sqrt(mul_r * mul_r + mul_i * mul_i);\r\n}<\/code><\/pre>\n<div id=\"magicdomid62\" class=\"\"><span class=\"\">This optimization works by looking at one object, then determin<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">ing<\/span><span class=\"\"> if th<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">e <\/span><span class=\"\">object never escape<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">s<\/span><span class=\"author-a-2z73zcz74zraz83zz88zez68zhz77zluz86zz76z\"> (detailed below)<\/span><span class=\"\">, that all properties are known, and <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">that <\/span><span class=\"\">it is not mixed with other objects.<\/span><\/div>\n<div id=\"magicdomid63\" class=\"\">&nbsp;<\/div>\n<div id=\"magicdomid66\" class=\"\"><span class=\"\">Once all these predicates are validated, this optimization emulates the content of the memory of the object while traversing the control flow graph of the program<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">, t<\/span><span class=\"\">hereby replacing all property accesses <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">with reads of <\/span><span class=\"\">local variables.<\/span><\/div>\n<div id=\"magicdomid67\" class=\"\">&nbsp;<\/div>\n<div id=\"magicdomid73\" class=\"\"><span class=\"\">Once all property accesses are removed, the alloca<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">ted<\/span><span class=\"\"> object is used <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">only <\/span><span class=\"\">by a few <a href=\"https:\/\/blog.mozilla.org\/javascript\/2014\/07\/15\/ionmonkey-optimizing-away\/\">recover instructions<\/a><\/span> <span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">that<\/span><span class=\"\"> are capturing the object state at across the control flow graph. Each object state is an instruction <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">that<\/span><span class=\"\"> serve<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">s<\/span><span class=\"\"> no purpose during the execution, but uses the Recover Instructions mechanism to set the content of the properties on <\/span><span class=\"author-a-2z73zcz74zraz83zz88zez68zhz77zluz86zz76z\">the deoptimization path (bailout) to Baseline compiled code<\/span><span class=\"\">. At the end, the Sink \/<\/span> <span class=\"\">Dead Code Elimination phase<\/span><span class=\"author-a-2z73zcz74zraz83zz88zez68zhz77zluz86zz76z\">s<\/span><span class=\"\"> convert the object allocation into a recovered instruction, as it is only used by the object state instructions.<\/span><\/div>\n<div id=\"magicdomid74\" class=\"\">&nbsp;<\/div>\n<h3 id=\"magicdomid75\"><span class=\"\">Escape Analysis<\/span><\/h3>\n<div id=\"magicdomid76\" class=\"\">&nbsp;<\/div>\n<div id=\"magicdomid78\" class=\"\"><span class=\"\">One of the biggest limitation of Scalar Replacement is that it is limited to objects <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">that do not escape<\/span><span class=\"\">.<\/span><\/div>\n<div id=\"magicdomid79\" class=\"\">&nbsp;<\/div>\n<div id=\"magicdomid81\" class=\"\"><span class=\"\">We have multiple way<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">s<\/span> <span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">for an object to escape:<\/span><\/div>\n<ul>\n<li><span class=\"\">a function call.<\/span>\n<pre><code class=\"js\">\r\nescape({ bar: 1, baz: 2 });<\/code><\/pre>\n<\/li>\n<li id=\"magicdomid87\"><span class=\"\">\u00a0a returned value.<\/span>\n<pre><code class=\"js\">\r\nfunction next() {\r\n    return { done: false, value: 0};\r\n}<\/code><\/pre>\n<\/li>\n<li id=\"magicdomid95\"><span class=\"\">an escaped object property.<\/span>\n<pre><code class=\"js\">\r\nescaped_obj = { property: obj };\r\nescape(escaped_obj);\r\n<\/code><\/pre>\n<\/li>\n<\/ul>\n<div id=\"magicdomid104\" class=\"\"><span class=\"\">The problem of <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">an <\/span><span class=\"\">escaped object is that we have no idea how the object would be manipulated, thus we cannot safely replace the properties of the object<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\"> with <\/span><span class=\"\">local variables.<\/span><\/div>\n<div id=\"magicdomid105\" class=\"\">&nbsp;<\/div>\n<div id=\"magicdomid108\" class=\"\"><span class=\"\">The good news is that we already have <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">an<\/span><span class=\"\"> optimization phase, called Inlining, <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">that<\/span><span class=\"\"> already tak<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">es<\/span><span class=\"\"> care of <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">inserting <\/span><span class=\"\">the code of smaller function<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">s<\/span><span class=\"\"> in<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">to<\/span><span class=\"\"> the body of the<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">ir<\/span> <span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">calling <\/span><span class=\"\">function<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">s<\/span><span class=\"\">, as long as they are frequently used.<\/span><\/div>\n<div id=\"magicdomid109\" class=\"\">&nbsp;<\/div>\n<div id=\"magicdomid113\" class=\"\"><span class=\"\">Inlining has some limitations, such as the size <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">and hotness <\/span><span class=\"\">of the function. <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">For example<\/span><span class=\"\">, it might be possible <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">for <\/span><span class=\"\">scalar replacement <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">to <\/span><span class=\"\">never trigger if the object escape<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">s <\/span><span class=\"\">in an unused branch, such as in exception handling or logging code.<\/span><\/div>\n<div id=\"magicdomid114\" class=\"\">&nbsp;<\/div>\n<div id=\"magicdomid115\" class=\"\">\n<pre><code class=\"js\">function doSomething(obj) {\r\n    if (theHighlyUnlikelyHappened())\r\n        throw new Error(\"aaaaaaahhh!!\", obj);\r\n}\r\n<\/code><\/pre>\n<\/div>\n<div id=\"magicdomid124\" class=\"\"><span class=\"\">Fortunately, this kind of problem <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">is <\/span><span class=\"\">addressed by <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">the <\/span><span class=\"\">Branch Pruning optimization de<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">s<\/span><span class=\"\">cribed in the next section.<\/span><\/div>\n<div id=\"magicdomid125\" class=\"\">&nbsp;<\/div>\n<h3 id=\"magicdomid126\"><span class=\"\">Mixing Objects<\/span><span class=\"author-a-2z73zcz74zraz83zz88zez68zhz77zluz86zz76z\"> from Multiple Allocation Sites<\/span><\/h3>\n<div id=\"magicdomid127\" class=\"\">&nbsp;<\/div>\n<div id=\"magicdomid131\" class=\"\"><span class=\"\">One other limitation of Scalar Replacement is that we have to be able to identify a single allocation<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\"> that <\/span><span class=\"\">dominat<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">es<\/span><span class=\"author-a-2z73zcz74zraz83zz88zez68zhz77zluz86zz76z\"> (i-e in the same block or in any enclosing block)<\/span><span class=\"\"> the rest of <\/span><span class=\"author-a-2z73zcz74zraz83zz88zez68zhz77zluz86zz76z\">its uses<\/span><span class=\"\">.\u00a0 For example<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">,<\/span><span class=\"\"> the following code causes problem because at the end of the then-block, we do not know which branch the allocated object comes from.<\/span><\/div>\n<div id=\"magicdomid132\" class=\"\">&nbsp;<\/div>\n<div id=\"magicdomid133\" class=\"\">\n<pre><code class=\"js\">function dummy() {\r\n    var obj = { done: false }; \/\/ (a)\r\n    if (len == max)\r\n        obj = { done: true }; \/\/ (b)\r\n    if (obj.done) \/\/ obj comes from either (a) or (b)\r\n        console.log(\"We are done! \\o\/\");\r\n}<\/code><\/pre>\n<\/div>\n<div id=\"magicdomid147\" class=\"\"><span class=\"\">This issue also appears in the case of <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">a <\/span><span class=\"\">returned object.\u00a0 When a function is inlined, all the return statements are funneled into <\/span><span class=\"author-a-2z73zcz74zraz83zz88zez68zhz77zluz86zz76z\">a return<\/span><span class=\"\"> block.\u00a0 Thus, <\/span><span class=\"author-a-2z73zcz74zraz83zz88zez68zhz77zluz86zz76z\">S<\/span><span class=\"\">calar <\/span><span class=\"author-a-2z73zcz74zraz83zz88zez68zhz77zluz86zz76z\">R<\/span><span class=\"\">eplacement is not capable <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">of<\/span><span class=\"\"> mix<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">ing<\/span><span class=\"\"> multiple objects<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">&#8216;<\/span><span class=\"\"> allocations.\u00a0 This problem occur<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">s<\/span><span class=\"\"> in the <code>next<\/code> function of iterators<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">:<\/span><\/div>\n<div id=\"magicdomid148\" class=\"\">&nbsp;<\/div>\n<div id=\"magicdomid149\" class=\"\">\n<pre><code class=\"js\">function next() {\r\n    if (this.idx &lt; this.len)\r\n        return { value: this.getValue(idx), done: false };\r\n    return { done: true };\r\n}<\/code><\/pre>\n<\/div>\n<div id=\"magicdomid159\" class=\"\"><span class=\"author-a-2z73zcz74zraz83zz88zez68zhz77zluz86zz76z\">As long as all properties are known<\/span><span class=\"\">, these can be transformed quite easily, by creating a single object allocation ahead of the condition and mutating the object, while returning the same object from all paths<\/span><span class=\"author-a-2z73zcz74zraz83zz88zez68zhz77zluz86zz76z\">.<\/span><\/div>\n<div id=\"magicdomid160\" class=\"\">&nbsp;<\/div>\n<div id=\"magicdomid161\" class=\"\">\n<pre><code class=\"js\">function next() {\r\n    var obj = { value: undefined, done: false };\r\n    if (this.idx &lt; this.len) {\r\n        obj.value = this.getValue(idx);\r\n        return obj;\r\n    }\r\n    obj.done = true;\r\n    return obj;\r\n}<\/code><\/pre>\n<\/div>\n<div id=\"magicdomid178\" class=\"\"><span class=\"\">This problem actually occur<\/span><span class=\"author-a-2z73zcz74zraz83zz88zez68zhz77zluz86zz76z\">r<\/span><span class=\"\">ed in the self-hosted code of <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">the <\/span><span class=\"\">Array iterator<\/span><span class=\"author-a-2z73zcz74zraz83zz88zez68zhz77zluz86zz76z\">&#8216;s<\/span><code><span class=\"\"> next<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">()<\/span><\/code><span class=\"\"> function, used by for-of.\u00a0 At first we rewrote it as shown in the example above, but to properly handle <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Mozilla\/Projects\/SpiderMonkey\/Compartments\">the security model<\/a> of SpiderMonkey, this trick was not enough<\/span><span class=\"author-a-2z73zcz74zraz83zz88zez68zhz77zluz86zz76z\">. The security model requires <\/span><span class=\"\">an extra branch which add<\/span><span class=\"author-a-2z73zcz74zraz83zz88zez68zhz77zluz86zz76z\">s<\/span><span class=\"\"> a new return <\/span><span class=\"author-a-2z73zcz74zraz83zz88zez68zhz77zluz86zz76z\">statement <\/span><span class=\"\">with a different object, which <\/span><span class=\"author-a-2z73zcz74zraz83zz88zez68zhz77zluz86zz76z\">cannot be merged above<\/span><span class=\"\">.<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\"> Fortunately<\/span><span class=\"\">, this issue goes away with Branch Pruning, as we w<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">ill <\/span><span class=\"\">see below.<\/span><\/div>\n<div id=\"magicdomid179\" class=\"\">&nbsp;<\/div>\n<h3 id=\"magicdomid180\"><span class=\"\">Known Properties<\/span><\/h3>\n<div id=\"magicdomid181\" class=\"\">&nbsp;<\/div>\n<div id=\"magicdomid185\" class=\"\"><span class=\"\">Another limitation of Scalar Replacement is the <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">requirement<\/span><span class=\"\"> to identify propert<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">y <\/span><span class=\"author-a-2z73zcz74zraz83zz88zez68zhz77zluz86zz76z\">accesses<\/span><span class=\"\"> at compil<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">e<\/span><span class=\"\"> time. This implies that one cannot expect to have a working Scalar Replacement <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">for<\/span><span class=\"\"> a loop iterating over the properties of an object, or the indexes of an array.<\/span><\/div>\n<div id=\"magicdomid186\" class=\"\">&nbsp;<\/div>\n<div id=\"magicdomid187\" class=\"\">\n<pre><code class=\"js\">function norm1(vec) {\r\n    var sum = 0;\r\n    for (var i = 0; i &lt; vec.length; i++)\r\n        sum += vec[i];\r\n    return sum;\r\n}<\/code><\/pre>\n<\/div>\n<div id=\"magicdomid197\" class=\"\"><span class=\"\">This is one case where <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">a <\/span><span class=\"\">Loop Unrolling optimization might be useful in the future.<\/span><\/div>\n<div id=\"magicdomid198\" class=\"\">&nbsp;<\/div>\n<h3 id=\"magicdomid199\"><span class=\"\">Lambdas<\/span><\/h3>\n<div id=\"magicdomid200\" class=\"\">&nbsp;<\/div>\n<div id=\"magicdomid203\" class=\"\"><span class=\"\">Lambdas are one of the cases where Scalar Replacement makes a lot of sense, especially in SpiderMonkey <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">where<\/span><span class=\"\"><a href=\"https:\/\/blog.mozilla.org\/luke\/2012\/10\/02\/optimizing-javascript-variable-access\/\"> the scope chain<\/a> <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">uses <\/span><span class=\"\">objects as the underlying representation.<\/span><\/div>\n<div id=\"magicdomid204\" class=\"\">&nbsp;<\/div>\n<div id=\"magicdomid208\" class=\"\"><span class=\"\">Each time you execute code <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">that<\/span><span class=\"\"> gives a lambda literal as <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">an <\/span><span class=\"\">argument, a new function is created.\u00a0 This new function holds a pointer to its function environment, which itself hold<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">s<\/span><span class=\"\"> a pointer to the scope chain of the enclosing function.<\/span><\/div>\n<div id=\"magicdomid209\" class=\"\">&nbsp;<\/div>\n<div id=\"magicdomid212\" class=\"\"><span class=\"\">In cases where <em>none<\/em> of the lambdas within a function escape, we can use scalar replacement to optimize scope chain accesses within the inlined code of the lambdas.<\/span><\/div>\n<div id=\"magicdomid213\" class=\"\">&nbsp;<\/div>\n<div id=\"magicdomid218\" class=\"\"><span class=\"\">For example, in the following code, the Inlining will add the code for the <code>forEach<\/code> function, and <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">the <\/span><span class=\"\">code of the lambda given as argument to the <\/span><code><span class=\"\">forEach<\/span><\/code> <span class=\"\">function<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">, into the caller<\/span><span class=\"\">. Then Scalar Replacement will detect that the lambda <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">does not <\/span><span class=\"\">escape, <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">since <\/span><span class=\"\"><code>forEach<\/code> and the lambda are inlined, and <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">it will <\/span><span class=\"\">replace the scope chain holding the captured <code>sum<\/code> variable<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\"> with<\/span><span class=\"\"> a local variable.<\/span><\/div>\n<div id=\"magicdomid219\" class=\"\">&nbsp;<\/div>\n<div id=\"magicdomid220\" class=\"\">\n<pre><code class=\"js\">function norm1(vec) {\r\n    var sum = 0;\r\n    vec.forEach((x) =&gt; { sum += x; });\r\n    return sum;\r\n}<\/code><\/pre>\n<\/div>\n<div id=\"magicdomid230\" class=\"\"><span class=\"\">At the same time, the scope chain allocation as well as the new function allocation holding it<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\"> will be<\/span><span class=\"\"> moved to <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">the <\/span><span class=\"\">bailout path.\u00a0 Thus, we <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">will <\/span><span class=\"\">no longer do any allocation to execute this function.\u00a0 In this case, Scalar Replacement makes this <code>forEach<\/code> call as fast as the C-like for loop equivalent.<br \/>\n<\/span><\/div>\n<div id=\"magicdomid231\" class=\"\">&nbsp;<\/div>\n<div id=\"magicdomid234\" class=\"\"><span class=\"\">Scalar Replacement has a lot of pitfalls, but when all the conditions are met, it can remove tons of allocations while allowing JavaScript developers to use higher level<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">s<\/span><span class=\"\"> of abstraction.<\/span><\/div>\n<div id=\"magicdomid235\" class=\"\">&nbsp;<\/div>\n<div id=\"magicdomid238\" class=\"\"><span class=\"\">As soon as we make generic function<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">s<\/span><span class=\"\">, it becomes quite hard to avoid these pitfalls. Just the fact that the code is present, even if it is never executed<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">,<\/span><span class=\"\"> causes these pitfalls to appear.<\/span><\/div>\n<div id=\"magicdomid239\" class=\"\">&nbsp;<\/div>\n<h2 id=\"magicdomid240\"><span class=\"\">Branch Pruning<\/span><\/h2>\n<div id=\"magicdomid241\" class=\"\">&nbsp;<\/div>\n<div id=\"magicdomid245\" class=\"\"><span class=\"\">The goal of Branch Pruning is to remove unused branches. This is similar to the badly named Profiler Guided Optimization<\/span><span class=\"author-a-2z73zcz74zraz83zz88zez68zhz77zluz86zz76z\"> (PGO<\/span><span class=\"\">) phase that we have in static compilers, except that instead of only moving infrequently used branches<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\"> out of the main code path<\/span><span class=\"\">, we remove them <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">entirely <\/span><span class=\"\">from IonMonkey&#8217;s generated code.<\/span><\/div>\n<div id=\"magicdomid246\" class=\"\">&nbsp;<\/div>\n<div id=\"magicdomid253\" class=\"\"><span class=\"\">To do so, we instrument SpiderMonkey to count the number of time each block of code g<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">ets<\/span><span class=\"\"> executed. Then, when compiling the code, we check based on hand-crafted heuristics w<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">h<\/span><span class=\"\">ether a block of code should be removed or not. The heuristics select branches <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">that<\/span> <span class=\"author-a-2z73zcz74zraz83zz88zez68zhz77zluz86zz76z\">have never been executed, <\/span><span class=\"\">are too complex<\/span><span class=\"author-a-2z73zcz74zraz83zz88zez68zhz77zluz86zz76z\"> (i-e store values in memory, make calls, have a large number of instructions)<\/span><span class=\"\"> and have predecessors with a large numbers of executions. If a block should be removed, then we replace the branch <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">with<\/span><span class=\"\"> a bailout<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\"> that<\/span><span class=\"\"> will fall<\/span> <span class=\"\">back <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">to<\/span><span class=\"\"> the Baseline compiled code to resume the execution.<\/span><\/div>\n<div id=\"magicdomid254\" class=\"\">&nbsp;<\/div>\n<div id=\"magicdomid257\" class=\"\"><span class=\"\">This optimization alone does not bring much performance improvement. At best, it can <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">speed up <\/span><span class=\"\">the compiler by removing <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">a <\/span><span class=\"\">chunk of the workload, and help the instruction cache of the CPU by removing instructions from the pipeline.<\/span><\/div>\n<div id=\"magicdomid258\" class=\"\">&nbsp;<\/div>\n<div id=\"magicdomid261\" class=\"\"><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">However, <\/span><span class=\"\">Branch Pruning <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">helps <\/span><span class=\"\">the other phases of IonMonkey. By removing unused branches, we improve other optimizations such as Scalar Replacement, Loop Invariant Code Motion, <\/span><span class=\"author-a-2z73zcz74zraz83zz88zez68zhz77zluz86zz76z\">etc.<\/span><\/div>\n<div id=\"magicdomid262\" class=\"\">&nbsp;<\/div>\n<div id=\"magicdomid265\" class=\"\"><span class=\"\">Thus code <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">that<\/span><span class=\"\"> has to handle <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">unlikely <\/span><span class=\"\">error cases, such as<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\"> in <\/span><span class=\"\">the following, can be modified by Branch Pruning to re<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">move<\/span><span class=\"\"> the block <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">that has <\/span><span class=\"\">never executed <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">yet<\/span><span class=\"\">.<\/span><\/div>\n<div id=\"magicdomid266\" class=\"\">&nbsp;<\/div>\n<div id=\"magicdomid267\" class=\"\">\n<pre><code class=\"js\">function doSomething(obj) {\r\n    if (theHighlyUnlikelyHappened())\r\n        throw new Error(\"aaaaaaahhh!!\", obj);\r\n}<\/code><\/pre>\n<\/div>\n<div id=\"magicdomid280\" class=\"\"><span class=\"\">In this example, IonMonkey will convert the then-block into a path which no longer merges back in the control flow graph, and <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">instead does a <\/span><span class=\"\">bailout to Baseline. In JavaScript terms, this would be similar to doing a yield while we are in IonMonkey&#8217;s compiled code and resuming the execution in Baseline&#8217;s compiled code. Baseline&#8217;s compiled code still has the instruction for running the exception handling code.<\/span><\/div>\n<div id=\"magicdomid281\" class=\"\">&nbsp;<\/div>\n<div id=\"magicdomid282\" class=\"\">\n<pre><code class=\"js\">\/\/ IonMonkey's compiled code\r\nfunction doSomething(obj) {\r\n    if (theHighlyUnlikelyHappened())\r\n        bailout; \/\/ yield and resume in Baseline compiled code.\r\n}<\/code><\/pre>\n<\/div>\n<h3 id=\"magicdomid290\"><span class=\"\">Frameworks<\/span><\/h3>\n<div id=\"magicdomid291\" class=\"\">&nbsp;<\/div>\n<div id=\"magicdomid295\" class=\"\"><span class=\"\">In today&#8217;s usage of JavaScript, a single page uses multiple framework<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">s<\/span><span class=\"\">. These frameworks are likely made to handle more than one use case. As a user of these frameworks, you are probably only using a few, either by convention or by habit.<\/span><\/div>\n<div id=\"magicdomid296\" class=\"\">&nbsp;<\/div>\n<div id=\"magicdomid299\" class=\"\"><span class=\"\">The prom<\/span><span class=\"author-a-2z73zcz74zraz83zz88zez68zhz77zluz86zz76z\">ise<\/span><span class=\"\"> of Branch Pruning is that all the branches of code that you are not using at all w<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">ill<\/span><span class=\"\"> be removed. Thus unused branches would not prevent optimizations.<\/span><\/div>\n<div id=\"magicdomid300\" class=\"\">&nbsp;<\/div>\n<div id=\"magicdomid305\" class=\"\"><span class=\"\">For example, as mentioned in <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">the <\/span><span class=\"\">Scalar Replacement optimization, <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">the <\/span><a href=\"https:\/\/searchfox.org\/mozilla-central\/search?q=function%20ArrayIteratorNext&amp;path=js\/src\/\"><span class=\"\">Array iterator&#8217;s <code>next<\/code><\/span><code><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">()<\/span><\/code><\/a><span class=\"\"> function has an extra branch to support <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Mozilla\/Projects\/SpiderMonkey\/Compartments\">the security model<\/a> of SpiderMonkey.\u00a0 This adds an extra branch <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">that <\/span><span class=\"\">is unlikely to be used by most website<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">s<\/span><span class=\"\">, thus Branch Pruning is able to remove this branch and replace it <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">with<\/span><span class=\"\"> a bailout.<\/span><\/div>\n<div id=\"magicdomid306\" class=\"\">&nbsp;<\/div>\n<div id=\"magicdomid310\" class=\"\"><span class=\"\">As this branch is replaced by a bailout, this waive<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">s<\/span><span class=\"\"> the limitation preventing Scalar Replacement.\u00a0 Thus, the fact that Branch Pruning removes code enable<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">s <\/span><span class=\"\">Scalar Replacement to reduce the memory allocations, and also optimize the execution <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">of<\/span><span class=\"\"> for-of loops.<\/span><\/div>\n<div id=\"magicdomid311\" class=\"\">&nbsp;<\/div>\n<div id=\"magicdomid1346\" class=\"ace-line\"><span class=\"author-a-2z73zcz74zraz83zz88zez68zhz77zluz86zz76z\">The following histogram represents the relative speed of the <a href=\"https:\/\/github.com\/h4writer\/arewefastyet\/blob\/master\/benchmarks\/misc\/tests\/assorted\/misc-basic-array-forof.js\">for-of micro-benchmark<\/a> compared against the same <a href=\"https:\/\/github.com\/h4writer\/arewefastyet\/blob\/master\/benchmarks\/misc\/tests\/assorted\/misc-basic-array.js\">for loop written in C-style<\/a>.\u00a0 In addition, we compare with the improvements provided by Scalar Replacement (SR), Branch Pruning (BP), and with both enabled (SR+BP).\u00a0 This highlights that these two optimizations are better than the sum of their individual contributions.\u00a0 These optimizations are dividing the time taken by for-of loops by a factor of 2.5x.<\/span><\/div>\n<div id=\"magicdomid1263\" class=\"ace-line\"><\/div>\n<div class=\"ace-line\">\u00a0<img decoding=\"async\" loading=\"lazy\" class=\"alignnone\" src=\"https:\/\/nbp.github.io\/slides\/VMM\/BranchPruning\/pictures\/sr-n-bp.svg\" width=\"600\" height=\"480\" \/><\/div>\n<div id=\"magicdomid319\" class=\"\">&nbsp;<\/div>\n<h2 id=\"magicdomid320\"><span class=\"\">Future Work<\/span><\/h2>\n<div id=\"magicdomid321\" class=\"\">&nbsp;<\/div>\n<div id=\"magicdomid327\" class=\"\"><span class=\"\">Scalar Replacement and Branch Pruning, when combined, are able to remove a lot of code<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\"> and<\/span><span class=\"\"> allocations, and <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">are <\/span><span class=\"\">able to improve the speed of frameworks such as ES6 for-of loops (only a factor of 2<\/span><span class=\"author-a-2z73zcz74zraz83zz88zez68zhz77zluz86zz76z\">.8x<\/span><span class=\"\"> behind a C-like for loop). To make for-of loops as fast as C-like for loops, we would <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">need <\/span><span class=\"\">to remove the bounds checks on the array element indexes, as well as add support for already allocated object<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">s<\/span><span class=\"\"> to Scalar Replacement.<\/span><\/div>\n<div id=\"magicdomid328\" class=\"\">&nbsp;<\/div>\n<div id=\"magicdomid333\" class=\"\"><span class=\"\">Branch Pruning heuristics are too conservative today, <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">meaning <\/span><span class=\"\">that we w<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">ill <\/span><span class=\"\">not attempt to remove branches unless we are confident that the branch is not going to be used. This problem comes from the fact that bailouts are costly. Hopefully, this w<\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">ill be <\/span><span class=\"\">addressed in the future <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">in<\/span><span class=\"\"> a project <\/span><span class=\"author-a-94z70z5z84zz86zwtz85z76kwd3k\">with the code name<\/span><span class=\"\"> ThreeHeadedMonkey.<\/span><\/div>\n<div id=\"magicdomid334\" class=\"\">&nbsp;<\/div>\n<div id=\"magicdomid336\" class=\"\"><span class=\"author-a-2z73zcz74zraz83zz88zez68zhz77zluz86zz76z\">Thanks to the blog post reviewers, with special thanks to Steve Fink, and to all the SpiderMonkey team for helping with the Scalar Replacement and Branch Pruning implementations.<\/span><\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>\u201cProgrammers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered.\u201d \u2014 Donald Knuth This &hellip; <a class=\"go\" href=\"https:\/\/blog.mozilla.org\/javascript\/2016\/07\/05\/ionmonkey-evil-on-your-behalf\/\">Continue reading<\/a><\/p>\n","protected":false},"author":564,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"_links":{"self":[{"href":"https:\/\/blog.mozilla.org\/javascript\/wp-json\/wp\/v2\/posts\/783"}],"collection":[{"href":"https:\/\/blog.mozilla.org\/javascript\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.mozilla.org\/javascript\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/javascript\/wp-json\/wp\/v2\/users\/564"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/javascript\/wp-json\/wp\/v2\/comments?post=783"}],"version-history":[{"count":0,"href":"https:\/\/blog.mozilla.org\/javascript\/wp-json\/wp\/v2\/posts\/783\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.mozilla.org\/javascript\/wp-json\/wp\/v2\/media?parent=783"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.mozilla.org\/javascript\/wp-json\/wp\/v2\/categories?post=783"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.mozilla.org\/javascript\/wp-json\/wp\/v2\/tags?post=783"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}