How to find Node.js Performance Optimization Killers

Vladimir de Turckheim

Lead Node.js engineer @ Sqreen​ (ex. Steamulo, Secway)

Outline

  • Introduction: V8?
  • Anatomy of an optimization...
  • ... followed by a deoptimization
  • Non-optimization
  • Conclusion

V8?

V8 is a key component of Node.js: it executes the JS code

V8 dates

  • 2008: Release
  • 2010: Crankshaft
  • 2015-2017: TurboFan
  • 2017: Ignition

In V8, the code is optimized dynamically; this means that the code is optimized according to its runtime behavior.

Credit: Google

Credit: Google

In this talk we will focus on the pre-Ignition versions of V8

Anatomy of an optimization...

// index.js

function myFunc(nb) {  
    return nb + nb;
}

for (let i = 0; i < 2000; ++i) {  
    myFunc(i);
}
$ node --trace-opt index.js | grep myFunc
[marking 0x2bc3091e7fc9 <JS Function myFunc (SharedFunctionInfo 0x1866a5c5eeb1)> 
for recompilation, reason: small function, ICs with typeinfo: 1/1(100%), 
generic ICs:0/1(0%)]

[compiling method 0x2bc3091e7fc9 <JS Function myFunc (SharedFunctionInfo 0x1866a5c5eeb1)> 
using Crankshaft]

[optimizing 0x2bc3091e7fc9 <JS Function myFunc (SharedFunctionInfo 0x1866a5c5eeb1)> - 
took 0.009, 0.068, 0.036 ms]

[completed optimizing 0x2bc3091e7fc9 <JS Function myFunc (SharedFunctionInfo 
0x1866a5c5eeb1)>]

The method's behavior is stable, it can be optimized.

... followed by a deoptimization

// index.js

function myFunc(nb) {  
    return nb + nb;
}

for (let i = 0; i < 2000; ++i) {  
    myFunc(i);
}

for (let i = 0; i < 2000; ++i) {  
    myFunc(i + '');
}
$ node --trace-opt --trace-deopt index.js | grep myFunc
[marking 0xc6b3e5e7fb9 <JS Function myFunc (SharedFunctionInfo 0x87d8115eec1)> 
for recompilation, reason: small function, ICs with typeinfo: 1/1 (100%), 
generic ICs: 0/1 (0%)]

...

[deoptimizing (DEOPT eager): begin 0xc6b3e5e7fb9 <JS Function myFunc 
(SharedFunctionInfo 0x87d8115eec1)> (opt #0) @1, FP to SP delta: 24, caller sp: 0x7ffe2cde6f40]

  reading input frame myFunc => node=4, args=2, height=1; inputs:

      0: 0xc6b3e5e7fb9 ; [fp - 16] 0xc6b3e5e7fb9 <JS Function myFunc 
(SharedFunctionInfo 0x87d8115eec1)>

  translating frame myFunc => node=4, height=0

    0x7ffe2cde6f10: [top + 0] <- 0xc6b3e5e7fb9 ;  function    
0xc6b3e5e7fb9 <JS Function myFunc (SharedFunctionInfo 0x87d8115eec1)>  (input #0)

Deoptimization-hell ?

Non-optimization

Those are subject to change in latest versions of V8

// try.js
function myFunc(nb) {  
    try {
        return nb + nb;
    }
    catch (err) {
        return err;
    }
}

for (let i = 0; i < 2000; ++i) {  
    myFunc(i);
}
$ node --trace-deopt --trace-opt try.js | grep myFunc 
[disabled optimization for 0x3a450705eeb1 <SharedFunctionInfo myFunc>, 
reason: TryCatchStatement]

Conclusion

V8 is not complicated to understand

JavaScript might be a permissive language, V8 does not like that

Turbofan will change the game for non-optimizations

What about chakracore ?

Going further

Any questions?

How to find Node.js Performance Optimization Killers

By Vladimir de Turckheim

How to find Node.js Performance Optimization Killers

  • 944