title: v8/chrome/node.js function inline
body: <p>How can I write functions that v8 will inline?</p>
  
  <p>Are there any tools to pre-compile my code to statically inline some functions? To statically transform functions and function calls to avoid capturing values?</p>
  
  <hr>
  
  <h3>Background</h3>
  
  <p>I noticed that the bottleneck of a JS program I wrote was a very simple function call: I was calling the function in a loop iterating millions of times, and manually inlining the function (i.e. replacing the function with its code) sped up the code by a few orders of magnitude.</p>
  
  <p>After that I tried to study the problem for a little, but couldn't infer rules on how function calls are optimized by v8, and how to write efficient functions.</p>
  
  <hr>
  
  <h3>Sample code: iterating 1 billion times</h3>
  
  <ol>
  <li><p>incrementing a counter:</p>
  
  <pre><code>let counter = 0;
  while(counter &lt; 1e9) ++counter;
  </code></pre>
  
  <p>it takes about <strong>~1 sec</strong>, on my system, both on Google Chrome/Chromium and v8. ~14 secs iterating <code>1e10</code> times.</p></li>
  <li><p>assigning to the counter the value of an incrementing function:</p>
  
  <pre><code>function incr(c) { return c+1; }
  let counter = 0;
  while(counter &lt; 1e9) counter = incr(counter);
  </code></pre>
  
  <p>it takes about <strong>~1 sec</strong>. ~14 secs iterating <code>1e10</code> times.</p></li>
  <li><p>calling a function (declared only once) that increments the captured counter:</p>
  
  <pre><code>let counter = 0;
  function incr() { ++counter; }
  while(counter &lt; 1e9) incr();
  </code></pre>
  
  <p>it takes about <strong>~3 sec</strong>. ~98 secs iterating <code>1e10</code> times.</p></li>
  <li><p>calling a (arrow) function defined in the loop that increments the captured counter:</p>
  
  <pre><code>let counter = 0;
  while(counter &lt; 1e9) (()=&gt;{ ++counter; })();
  </code></pre>
  
  <p>it takes about <strong>~24 secs</strong>. (I noticed that a named function or an arrow one makes no difference)</p></li>
  <li><p>calling a (arrow) function defined in the loop to increment the counter without capturing:</p>
  
  <pre><code>let counter = 0;
  while(counter &lt; 1e9) {
      const incr = (c)=&gt;c+1;
      counter = incr(counter);
  }
  </code></pre>
  
  <p>it takes about <strong>~22 secs</strong>.</p></li>
  </ol>
  
  <p>I'm surprised by the fact that:</p>
  
  <ul>
  <li><p>capturing a variable slows down the code. Why? Is this a general rule? Should I always avoid capturing variables in performance critical functions?</p></li>
  <li><p>the negative effects of capturing a variable grow a lot when iterating 1e10 times. What's going on there? If I had to take a wild guess I'd say that beyond 1^31 the variable changes type, and the function wasn't optimized for this?</p></li>
  <li><p>declaring a function in a loop slows down the code so much. v8 doesn't optimize the function at all? I thought it was smarter than that! I guess I should never declare functions in critical loops...</p></li>
  <li><p>it makes little difference if the function declared in a loop captures a variable or not. I guess capturing a variable is bad for optimized code, but not so bad for not optimized one?</p></li>
  <li><p>given all of this, I'm actually surprised v8 can perfectly inline long-lasting non-capturing functions. I guess these are the only reliable ones performance-wise?</p></li>
  </ul>
  
  <hr>
  
  <h3>Edit 1: adding some extra snippets to expose extra weirdness.</h3>
  
  <p>I created a new file, with the following code inside:</p>
  
  <pre><code>const start = new Date();
  function incr(c) { return c+1; }
  let counter = 0;
  while(counter &lt; 1e9) counter = incr(counter);
  console.log( new Date().getTime() - start.getTime() );
  </code></pre>
  
  <p>It prints a value closed to <strong>~1 sec</strong>.</p>
  
  <p>Then I declared a new variable at the end of the file. Any variable works fine: just append <code>let x;</code> to that snipped. The code now took <strong>~12 secs</strong> to complete.</p>
  
  <p>If instead of using that <code>incr</code> function you just use <code>++counter</code> as in the very first snippet, the extra variable makes the performance degrade from ~1 sec to ~2.5 secs. Putting these snippets into functions, declaring other variables or changing the order of some statements sometimes improves the performance, while other times degrades it even further.</p>
  
  <ul>
  <li><p>WTF?</p></li>
  <li><p>I knew about weird effects like <a href="https://top.fse.guru/nodejs-a-quick-optimization-advice-7353b820c92e#.f48gjmvvv" rel="nofollow">this one</a>, and I've read a bunch of guides on how to optimize JS for v8. Still: WTF?!</p></li>
  <li><p>I played for a bit with the bottleneck of the JS program that made me start this research. I saw a difference of more than 4 orders of magnitude between implementations that I wouldn't have expected to be any different. I'm currently convinced that the performance of number-crunching algorithms in v8 is completely unpredictable and am going to rewrite the bottleneck in C and expose it as a function to v8.</p></li>
  </ul>