V8 Behind the Scenes: I+TF Launch and Declarative JavaScript
Remember: limiting yourself to a single engine and optimizing for that — or hoping for certain optimizations to happen in V8 — is certainly not the way to success.
Join the DZone community and get the full member experience.
Join For Freethe last month was pretty exciting. we finally turned on ignition and turbofan by default for chrome m59. it took two attempts, but it seems to stick now. overall, it went surprisingly well, despite the complexity and impact of this change.
as my site director used to put it : “changing from crankshaft to turbofan in chrome was like changing the engine of a f1 car at 250 km/h.”
note that with the switch, we also removed the
enable-v8-future
setting from
chrome://flags
, and instead added a kill switch
disable-v8-ignition-turbo
for the new configuration on chrome m59. this kill switch has three possible settings: default, disabled, and enabled. if you leave it to default, there’s a certain chance that you get the old compiler pipeline even on canary or dev channel due to a/b testing. set it to disabled to ensure that you get the new pipeline, or set it to enabled to go with crankshaft and full-codegen again.
the instructions for chrome m58 (currently in the beta channel) didn’t change. ignition and turbofan are still controlled by the
enable-v8-future
flag:
turning on the new configuration by default uncovered a couple of issues that weren’t known yet, so i was pretty busy helping to address those. but as far as i can tell, things look very promising now.
at the end of last month, i gave a presentation about turbofan: a new code generation architecture for v8 at the munich node.js user group meetup, talking a bit about things that will change with the new pipeline. most importantly, turning on the new pipeline greatly reduces the overall complexity of v8 and makes it easier to port to new architectures. what’s even more important from a user’s perspective is that it completely eliminates the need to worry about so-called optimization killers since turbofan is able to handle every language construct.
this doesn’t mean that all of a sudden using a weird language construct like
with
is blazingly fast, but it
does
mean that the presence of one of these optimization killers in a function no longer disables the optimizing compiler completely for that particular function.
source: turbofan: a new code generation architecture for v8 , mnug march '07 meetup, @bmeurer .
probably the most important changes are fully optimizable
try
-
catch
/
try
-
finally
statements, generators and
async
functions, and of course you can now use
let
and
const
without the risk of hitting the infamous
unsupported phi use of const or let variable
bailout.
one interesting and somewhat surprising follow-up was the discussion triggered by the performance advice i gave on what i called declarative javascript in my talk:
first of all, it seems that various people are confused by the terminology. it seems that using the terms “explicit vs. implicit” instead of “declarative vs. obscure” is easier to understand for many people, so just imagine those terms when staring at the slide above. the main controversy, however, was around whether it makes sense to use this advice or assume that the engine will optimize all of this under the hood.
given infinite resources, v8 could generate better code for the right-hand side, as well (and crankshaft used to do this in the past), where it only checks for
undefined
and objects, and deoptimizes on all other inputs. that reduces the number of checks in optimized code. however, it comes at a price: it can deoptimize the optimized code as soon as new values enter here, and the engine needs to track what kind of values were seen for
obj
in the baseline case, which requires time and memory.
but the reality is that v8 doesn’t have infinite resources. in fact, with more and more low-end android devices entering the mobile web, and less and less traffic coming from high-end mobile devices or desktops, it seems that v8 (and chrome) will have even fewer resources available in the future. reducing overhead and optimizing less aggressively has helped a lot in the past two years to significantly improve the experience on mobile.
i sometimes hear developers say that these startup issues don’t matter to node.js, and v8 shouldn’t penalize what they call server performance, but then the next thing i hear is them complaining that typescript, uglifyjs, webpack, babel, etc. run way too slow. this also runs on v8 and also suffers a lot from overhead during warmup, or too aggressive optimizations leading to frequent deoptimizations. i’ve seen a lot of evidence that reducing over-optimization has helped server workloads significantly, as for example measured by the node.js acmeair benchmark.
the throughput improved by over 45% mostly by reducing overhead in v8, accomplished via work guided mostly by client-side workloads. so, for typical server-side workloads things don’t look too different.
and there’s another important aspect that tends to be forgotten easily: v8 is not the only javascript engine you should care about. it’s certainly very dominant due to it’s use in chrome, node.js, android, and opera, but limiting yourself to a single engine and optimizing for that — or hoping for certain optimizations to happen in v8 — is certainly not the way to success.
speaking of the example above, whether or not
toboolean
is heavily optimized by the engine or not, and specifically what kind of feedback is tracked and consumed about the incoming values, is and should remain somewhat irrelevant. if you can write your hot code in a way that is optimizable independent of certain speculative optimizations, you should favor that way. for example, when you have a value
obj
that can either be
undefined
or an object, consider ruling out
undefined
explicit via:
if (obj !== undefined) { // … }
or even better:
if (obj !== void 0) { // … }
this might also help your future self understanding this code in two years, as with:
if (obj) { // … }
it’s not clear that all you intended to do back then was to rule out
undefined
for
obj
.
this is especially useful to keep in mind when using
||
and
&&
in javascript. for example i’ve seen developers using
||
to implement default parameters like this:
function foo(a, b) { a = a || "value"; b = b || 4; // … }
when asked whether the empty string is a valid input for
a
or
0
is a valid input for
b
, they were quite surprised to notice that they would rule out valid inputs this way. so don’t do this! instead use the es2015 feature:
function foo(a = "value", b = 4) { // … }
this only replaces
undefined
values with defaults, which is sane. there’s also an
interesting performance aspect
to using
&&
and
||
in javascript. i’ll see if i can do a series about writing explicit javascript with focus on performance aspects from the vms perspective, which might help to shed some light on this topic.
my usual disclaimer applies: i’m not a front-end engineer. i have different priorities than you might have, and there might be good reasons for you to ignore any kind of advice that i give here. that’s perfectly fine. also be careful with over-optimizing!
Published at DZone with permission of Benedikt Meurer, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments