From 762c7d31786221e5d67f4dd7a327a5b16efbe3a3 Mon Sep 17 00:00:00 2001 From: Leszek Swirski Date: Wed, 6 Dec 2023 23:48:03 +0100 Subject: [PATCH] Add maglev blog post --- src/_img/avatars/darius-mercadier.avif | Bin 0 -> 1101 bytes src/_img/avatars/darius-mercadier.jpg | Bin 0 -> 3870 bytes src/_img/avatars/darius-mercadier@2x.avif | Bin 0 -> 2339 bytes src/_img/avatars/darius-mercadier@2x.jpg | Bin 0 -> 10519 bytes src/_img/avatars/olivier-flueckiger.avif | Bin 0 -> 1803 bytes src/_img/avatars/olivier-flueckiger.jpg | Bin 0 -> 5212 bytes src/_img/avatars/olivier-flueckiger@2x.avif | Bin 0 -> 3520 bytes src/_img/avatars/olivier-flueckiger@2x.jpg | Bin 0 -> 13251 bytes src/_img/maglev/I-IS-IT-IST-ISTM.svg | 1 + src/_img/maglev/I-IS-IT-IST.svg | 1 + src/_img/maglev/I-IT.svg | 1 + src/_img/maglev/compile-time.svg | 1607 +++++++++++++++++++ src/_img/maglev/graph.svg | 43 + src/_includes/layouts/post.njk | 6 +- src/blog/maglev.md | 151 ++ 15 files changed, 1809 insertions(+), 1 deletion(-) create mode 100644 src/_img/avatars/darius-mercadier.avif create mode 100644 src/_img/avatars/darius-mercadier.jpg create mode 100644 src/_img/avatars/darius-mercadier@2x.avif create mode 100644 src/_img/avatars/darius-mercadier@2x.jpg create mode 100644 src/_img/avatars/olivier-flueckiger.avif create mode 100644 src/_img/avatars/olivier-flueckiger.jpg create mode 100644 src/_img/avatars/olivier-flueckiger@2x.avif create mode 100644 src/_img/avatars/olivier-flueckiger@2x.jpg create mode 100644 src/_img/maglev/I-IS-IT-IST-ISTM.svg create mode 100644 src/_img/maglev/I-IS-IT-IST.svg create mode 100644 src/_img/maglev/I-IT.svg create mode 100644 src/_img/maglev/compile-time.svg create mode 100644 src/_img/maglev/graph.svg create mode 100644 src/blog/maglev.md diff --git a/src/_img/avatars/darius-mercadier.avif b/src/_img/avatars/darius-mercadier.avif new file mode 100644 index 0000000000000000000000000000000000000000..05c6df817de777fd6f7b28f5a25bb598405adeeb GIT binary patch literal 1101 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 Xbt1lut!uEZ3_IU_>~_mrgF5y<*G&-? literal 0 HcmV?d00001 diff --git a/src/_img/avatars/darius-mercadier@2x.avif b/src/_img/avatars/darius-mercadier@2x.avif new file mode 100644 index 0000000000000000000000000000000000000000..915e682dcff1604ee124af2312650052d3e67fa7 GIT binary patch literal 2339 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 literal 0 HcmV?d00001 diff --git a/src/_img/avatars/darius-mercadier@2x.jpg b/src/_img/avatars/darius-mercadier@2x.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d85e70d25a7da87fd8ec2bc91cae9b9aa78873ae GIT binary patch literal 10519 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 literal 0 HcmV?d00001 diff --git a/src/_img/avatars/olivier-flueckiger.jpg b/src/_img/avatars/olivier-flueckiger.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1bd75e80e5c1e650d20a1af373e325062a42fda8 GIT binary patch literal 5212 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 z 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 J=dZWr{{cS5e!Bnw literal 0 HcmV?d00001 diff --git a/src/_img/maglev/I-IS-IT-IST-ISTM.svg b/src/_img/maglev/I-IS-IT-IST-ISTM.svg new file mode 100644 index 000000000..0e670559a --- /dev/null +++ b/src/_img/maglev/I-IS-IT-IST-ISTM.svg @@ -0,0 +1 @@ +050100150200250300350ScoreIgnitionIgnition + SparkplugIgnition + TurboFanIgnition + Sparkplug + TurboFanIgnition + Sparkplug + Maglev + TurboFan6493279302327JetStream050100150200250300350400450500ScoreIgnitionIgnition + SparkplugIgnition + TurboFanIgnition + Sparkplug + TurboFanIgnition + Sparkplug + Maglev + TurboFan246347376458486Speedometer \ No newline at end of file diff --git a/src/_img/maglev/I-IS-IT-IST.svg b/src/_img/maglev/I-IS-IT-IST.svg new file mode 100644 index 000000000..b8dbe91fc --- /dev/null +++ b/src/_img/maglev/I-IS-IT-IST.svg @@ -0,0 +1 @@ +050100150200250300350ScoreIgnitionIgnition + SparkplugIgnition + TurboFanIgnition + Sparkplug + TurboFan6493279302JetStream050100150200250300350400450500ScoreIgnitionIgnition + SparkplugIgnition + TurboFanIgnition + Sparkplug + TurboFan246347376458Speedometer \ No newline at end of file diff --git a/src/_img/maglev/I-IT.svg b/src/_img/maglev/I-IT.svg new file mode 100644 index 000000000..ce70bcfa3 --- /dev/null +++ b/src/_img/maglev/I-IT.svg @@ -0,0 +1 @@ +020406080100120140160180200220240260280ScoreIgnitionIgnition + TurboFan64279JetStream050100150200250300350400ScoreIgnitionIgnition + TurboFan246376Speedometer \ No newline at end of file diff --git a/src/_img/maglev/compile-time.svg b/src/_img/maglev/compile-time.svg new file mode 100644 index 000000000..331dd377c --- /dev/null +++ b/src/_img/maglev/compile-time.svg @@ -0,0 +1,1607 @@ + + + + + + + + 2023-12-06T00:22:07.864356 + image/svg+xml + + + Matplotlib v3.8.0, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/_img/maglev/graph.svg b/src/_img/maglev/graph.svg new file mode 100644 index 000000000..609f50476 --- /dev/null +++ b/src/_img/maglev/graph.svg @@ -0,0 +1,43 @@ + + + + + + + + + + 0x154a00199c01 <SharedFunctionInfo fib> (0x154a000487bd <String[6]: "fib.js">)│ 0 : LdaSmi [1] 30/34: Jump b6Block b6 127: ConstantGapMove(v10/n36 → [rsi|R|w32])│ 5 : JumpIfFalse [5]╭────31/38: BranchIfInt32Compare(LessThanOrEqual) [v28/n25:[rax|R|w32], v10/n36:[rsi|R|w32]] b7 b8Block b7 9 : Return╭───32/39: Jump b9╰─►Block b8 128: GapMove([rax|R|w32] → [rdi|R|w32]) 16 : SubSmi [1], [4] ↱ eager @39 (4 live vars) │ @16 (5 live vars) 33/41: Int32SubtractWithOverflow [v28/n25:[rax|R|w32], v10/n36:[rsi|R|w32]] → [rax|R|w32], live range: [33-34] 20 : CallUndefinedReceiver1 r0, r1, [5] 34/42: Int32ToNumber [v33/n41:[rax|R|w32]] → [r8|R|t], live range: [34-35] 129: GapMove([rdi|R|w32] → [rax|R|w32]) 130: ConstantGapMove(v2/n31 → [rdi|R|t]) 131: ConstantGapMove(v1/n32 → [rsi|R|t]) 132: ConstantGapMove(v4/n23 → [rdx|R|t]) 35/43: 🐢 CallKnownJSFunction(0x154a00199c01 <SharedFunctionInfo fib>) [v2/n31:[rdi|R|t], v1/n32:[rsi|R|t], v3/n33:[constant:v-1], v4/n23:[rdx|R|t], v34/n42:[r8|R|t]] → [rax|R|t] (spilled: [stack:4|t]), live range: [35-41] │ @39 (4 live vars) ↳ lazy @20 (4 live vars) 133: GapMove([stack:1|w32] → [rcx|R|w32]) + \ No newline at end of file diff --git a/src/_includes/layouts/post.njk b/src/_includes/layouts/post.njk index 7a035a4ed..cb9c546c8 100644 --- a/src/_includes/layouts/post.njk +++ b/src/_includes/layouts/post.njk @@ -29,7 +29,11 @@ layout: layouts/base.njk {% endfor %} {% endif %} -

Posted by {{ author | markdown | safe }}.

+

4 %} + style="clear:both" + {% endif %} + >Posted by {{ author | markdown | safe }}.

{% if tweet %}
Retweet this article! diff --git a/src/blog/maglev.md b/src/blog/maglev.md new file mode 100644 index 000000000..2b3ee3752 --- /dev/null +++ b/src/blog/maglev.md @@ -0,0 +1,151 @@ +--- +title: 'Maglev - V8’s Fastest Optimizing JIT' +author: '[Toon Verwaest](https://twitter.com/tverwaes), [Leszek Swirski](https://twitter.com/leszekswirski), [Victor Gomes](https://twitter.com/VictorBFG), Olivier Flückiger, Darius Mercadier, and Camillo Bruni — not enough cooks to spoil the broth' +avatars: + - toon-verwaest + - leszek-swirski + - victor-gomes + - olivier-flueckiger + - darius-mercadier + - camillo-bruni +date: 2023-12-05 +tags: + - JavaScript +description: "V8's newest compiler, Maglev, improves performance while reducing power consumption" +tweet: '' +--- + +In Chrome M117 we introduced a new optimizing compiler: Maglev. Maglev sits between our existing Sparkplug and TurboFan compilers, and fills the role of a fast optimizing compiler that generates good enough code, fast enough. + + +# Background + +Until 2021 V8 had two main execution tiers: Ignition, the interpreter; and [TurboFan](/docs/turboFan), V8’s optimizing compiler focused on peak performance. All JavaScript code is first compiled to ignition bytecode, and executed by interpreting it. During execution V8 tracks how the program behaves, including tracking object shapes and types. Both the runtime execution metadata and bytecode are fed into the optimizing compiler to generate high-performance, often speculative, machine code that runs significantly faster than the interpreter can. + +These improvements are clearly visible on benchmarks like [JetStream](https://browserbench.org/JetStream2.1/), a collection of traditional pure JavaScript benchmarks measuring startup, latency, and peak performance. TurboFan helps V8 run the suite 4.35x as fast! JetStream has a reduced emphasis on steady state performance compared to past benchmarks (like the [retired Octane benchmark](/blog/retiring-octane)), but due to the simplicity of many line items, the optimized code is still where most time is spent. + +[Speedometer](https://browserbench.org/Speedometer2.1/) is a different kind of benchmark suite than JetStream. It’s designed to measure a web app’s responsiveness by timing simulated user interactions. Instead of smaller static standalone JavaScript apps, the suite consists of full web pages, most of which are built using popular frameworks. Like during most web page loads, Speedometer line items spend much less time running tight JavaScript loops and much more executing a lot of code that interacts with the rest of the browser. + +TurboFan still has a lot of impact on Speedometer: it runs over 1.5x as fast! But the impact is clearly much more muted than on JetStream. Part of this difference results from the fact that full pages [just spend less time in pure JavaScript](/blog/real-world-performance#making-a-real-difference). But in part it’s due to the benchmark spending a lot of time in functions that don’t get hot enough to be optimized by TurboFan. + +![Web performance benchmarks comparing unoptimized and optimized execution](/_img/maglev/I-IT.svg) + +::: note +All the benchmark scores in this post were measured with Chrome 117.0.5897.3 on a 13” M2 Macbook Air. +::: + +Since the difference in execution speed and compile time between Ignition and TurboFan is so large, in 2021 we introduced a new baseline JIT called [Sparkplug](/blog/sparkplug). It’s designed to compile bytecode to equivalent machine code almost instantaneously. + +On JetStream, Sparkplug improves performance quite a bit compared to Ignition (+45%). Even when TurboFan is also in the picture we still see a solid improvement in performance (+8%). On Speedometer we see a 41% improvement over Ignition, bringing it close to TurboFan performance, and a 22% improvement over Ignition + TurboFan! Since Sparkplug is so fast, we can easily deploy it very broadly and get a consistent speedup. If code doesn’t rely solely on easily optimized, long-running, tight JavaScript loops, it’s a great addition. + +![Web performance benchmarks with added Sparkplug](/_img/maglev/I-IS-IT-IST.svg) + +The simplicity of Sparkplug imposes a relatively low upper limit on the speedup it can provide though. This is clearly demonstrated by the large gap between Ignition + Sparkplug and Ignition + TurboFan. + +This is where Maglev comes in, our new optimizing JIT that generates code that’s much faster than Sparkplug code, but is generated much faster than TurboFan can. + + +# Maglev: A Simple SSA-Based JIT compiler + +When we started this project we saw two paths forward to cover the gap between Sparkplug and TurboFan: either try to generate better code using the single-pass approach taken by Sparkplug, or build a JIT with an intermediate representation (IR). Since we felt that not having an IR at all during compilation would likely severely restrict the compiler, we decided to go with a somewhat traditional single-static-assignment (SSA) based approach, using a CFG (control flow graph) rather than TurboFan's more flexible but cache unfriendly sea-of-nodes representation. + +The compiler itself is designed to be fast and easy to work on. It has a minimal set of passes and a simple, single IR that encodes specialized JavaScript semantics. + + +## Prepass + +First Maglev does a prepass over the bytecode to find branch targets, including loops, and assignments to variables in loop. This pass also collects liveness information, encoding which values in which variables are still needed across which expressions. This information can reduce the amount of state that needs to be tracked by the compiler later. + + +## SSA + +![A printout of the Maglev SSA graph on the command line](/_img/maglev/graph.svg) + +Maglev does an abstract interpretation of the frame state, creating SSA nodes representing the results of expression evaluation. Variable assignments are emulated by storing those SSA nodes in the respective abstract interpreter register. In the case of branches and switches, all paths are evaluated. + +When multiple paths merge, values in abstract interpreter registers are merged by inserting so-called Phi nodes: value nodes that know which value to pick depending on which path was taken at runtime. + +Loops can merge variable values “back in time”, with the data flowing backwards from the loop end to the loop header, in the case when variables are assigned in the loop body. That’s where the data from the prepass comes in handy: since we already know which variables are assigned inside loops, we can pre-create loop phis before we even start processing the loop body. At the end of the loop we can populate the phi input with the correct SSA node. This allows the SSA graph generation to be a single forward pass, without needing to "fix up" loop variables, while also minimizing the amount of Phi nodes that need to be allocated. + + +## Known Node Information + +To be as fast as possible, Maglev does as much as possible at once. Instead of building a generic JavaScript graph and then lowering that during later optimization phases, which is a theoretically clean but computationally expensive approach, Maglev does as much as possible immediately during graph building. + +During graph building Maglev will look at runtime feedback metadata collected during unoptimized execution, and generate specialized SSA nodes for the types observed. If Maglev sees `o.x` and knows from the runtime feedback that `o` always has one specific shape, it will generate an SSA node to check at runtime that `o` still has the expected shape, followed by a cheap `LoadField` node which does a simple access by offset. + +Additionally, Maglev will make a side node that it now knows the shape of `o`, making it unnecessary to check the shape again later. If Maglev later encounters an operation on `o` that doesn't have feedback for some reason, this kind of information learned during compilation can be used as a second source of feedback. + +Runtime information can come in various forms. Some information needs to be checked at runtime, like the shape check previously described. Other information can be used without runtime checks by registering dependencies to the runtime. Globals that are de-facto constant (not changed between initialization and when their value is seen by Maglev) fall into this category: Maglev does not need to generate code to dynamically load and check their identity. Maglev can load the value at compile time and embed it directly into the machine code; if the runtime ever mutates that global, it'll also take care to invalidate and deoptimize that machine code. + +Some forms of information are “unstable”. Such information can only be used to the extent that the compiler knows for sure that it can’t change. For example, if we just allocated an object, we know it’s a new object and we can skip expensive write barriers entirely. Once there has been another potential allocation, the garbage collector could have moved the object, and we now need to emit such checks. Others are "stable": if we have never seen any object transition away from having a certain shape, then we can register a dependency on this event (any object transitioning away from that particular shape) and don’t need to recheck the shape of the object, even after a call to an unknown function with unknown side effects. + + +## Deoptimization + +Given that Maglev can use speculative information that it checks at runtime, Maglev code needs to be able to deoptimize. To make this work, Maglev attaches abstract interpreter frame state to nodes that can deoptimize. This state maps interpreter registers to SSA values. This state turns into metadata during code generation, providing a mapping from optimized state to unoptimized state. The deoptimizer interprets this data, reading values from the interpreter frame and machine registers and putting them into the required places for interpretation. This builds on the same deoptimization mechanism as used by TurboFan, allowing us to share most of the logic and take advantage of the testing of the existing system. + + +## Representation Selection + +JavaScript numbers represent, according to [the spec](https://tc39.es/ecma262/#sec-ecmascript-language-types-number-type), a 64-bit floating point value. This doesn't mean that the engine has to always store them as 64-bit floats though, especially since In practice many numbers are small integers (e.g. array indices). V8 tries to encode numbers as 31-bit tagged integers (internally called “Small Integers” or "Smi"), both to save memory (32bit due to [pointer compression](/blog/pointer-compression)), and for performance (integer operations are faster than float operations). + +To make numerics-heavy JavaScript code fast, it’s important that optimal representations are chosen for value nodes. Unlike the interpreter and Sparkplug, the optimizing compiler can unbox values once it knows their type, operating on raw numbers rather than JavaScript values representing numbers, and rebox values only if strictly necessary. Floats can directly be passed in floating point registers instead of allocating a heap object that contains the float. + +Maglev learns about the representation of SSA nodes mainly by looking at runtime feedback of e.g., binary operations, and propagating that information forwards through the Known Node Info mechanism. When SSA values with specific representations flow into Phis, a correct representation that supports all the inputs needs to be chosen. Loop phis are again tricky, since inputs from within the loop are seen after a representation should be chosen for the phi — the same "back in time" problem as for graph building. This is why Maglev has a separate phase after graph building to do representation selection on loop phis. + + +## Register Allocation + +After graph building and representation selection, Maglev mostly knows what kind of code it wants to generate, and is "done" from a classical optimization point of view. To be able to generate code though, we need to choose where SSA values actually live when executing machine code; when they're in machine registers, and when they're saved on the stack. This is done through register allocation. + +Each Maglev node has input and output requirements, including requirements on temporaries needed. The register allocator does a single forward walk over the graph, maintaining an abstract machine register state not too dissimilar from the abstract interpretation state maintained during graph building, and will satisfy those requirements, replacing the requirements on the node with actual locations. Those locations can then be used by code generation. + +First, a prepass runs over the graph to find linear live ranges of nodes, so that we can free up registers once an SSA node isn’t needed anymore. This prepass also keeps track of the chain of uses. Knowing how far in the future a value is needed can be useful to decide which values to prioritize, and which to drop, when we run out of registers. + +After the prepass, the register allocation runs. Register assignment follows some simple, local rules: If a value is already in a register, that register is used if possible. Nodes keep track of what registers they are stored into during the graph walk. If the node doesn’t yet have a register, but a register is free, it’s picked. The node gets updated to indicate it’s in the register, and the abstract register state is updated to know it contains the node. If there’s no free register, but a register is required, another value is pushed out of the register. Ideally, we have a node that’s already in a different register, and can drop this "for free"; otherwise we pick a value that won’t be needed for a long time, and spill it onto the stack. + +On branch merges, the abstract register states from the incoming branches are merged. We try to keep as many values in registers as possible. This can mean we need to introduce register-to-register moves, or may need to unspill values from the stack, using moves called “gap moves”. If a branch merge has a phi node, register allocation will assign output registers to the phis. Maglev prefers to output phis to the same registers as its inputs, to minimize moves. + +If more SSA values are live than we have registers, we’ll need to spill some values on the stack, and unspill them later. In the spirit of Maglev, we keep it simple: if a value needs to be spilled, it is retroactively told to immediately spill on definition (right after the value is created), and code generation will handle emitting the spill code. The definition is guaranteed to ‘dominate’ all uses of the value (to reach the use we must have passed through the definition and therefore the spill code). This also means that a spilled value will have exactly one spill slot for the entire duration of the code; values with overlapping lifetimes will thus have non-overlapping assigned spill slots. + +Due to representation selection, some values in the Maglev frame will be tagged pointers, pointers that V8’s GC understands and needs to consider; and some will be untagged, values that the GC should not look at. TurboFan handles this by precisely keeping track of which stack slots contain tagged values, and which contain untagged values, which changes during execution as slots are reused for different values. For Maglev we decided to keep things simpler, to reduce the memory required for tracking this: we split the stack frame into a tagged and an untagged region, and only store this split point. + + +## Code Generation + +Once we know what expressions we want to generate code for, and where we want to put their outputs and inputs, Maglev is ready to generate code. + +Maglev nodes directly know how to generate assembly code using a “macro assembler”. For example, a `CheckMap` node knows how to emit assembler instructions that compare the shape (internally called the “map”) of an input object with a known value, and to deoptimize the code if the object had a wrong shape. + +One slightly tricky bit of code handles gap moves: The requested moves created by the register allocator know that a value lives somewhere and needs to go elsewhere. If there’s a sequence of such moves though, a preceding move could clobber the input needed by a subsequent move. The Parallel Move Resolver computes how to safely perform the moves so that all values end up in the right place. + + +# Results + +So the compiler we just presented is both clearly much more complex than Sparkplug, and much simpler than TurboFan. How does it fare? + +In terms of compilation speed we’ve managed to build a JIT that’s roughly 10x slower than Sparkplug, and 10x faster than TurboFan. + +![Compile time comparison of the compilation tiers, for all functions compiled in JetStream](/_img/maglev/compile-time.svg) + +This allows us to deploy Maglev much earlier than we’d want to deploy TurboFan. If the feedback it relied upon ended up not being very stable yet, there’s no huge cost to deoptimizing and recompiling later. It also allows us to use TurboFan a little later: we’re running much faster than we’d run with Sparkplug. + +Slotting in Maglev between Sparkplug and TurboFan results in noticeable benchmark improvements: + +![Web performance benchmarks with Maglev](/_img/maglev/I-IS-IT-IST-ISTM.svg) + +We have also validated Maglev on real-world data, and see good improvements on [Core Web Vitals](https://web.dev/vitals/). + +Since Maglev compiles much faster, and since we can now afford to wait longer before we compile functions with TurboFan, this results in a secondary benefit that’s not as visible on the surface. The benchmarks focus on main-thread latency, but Maglev also significantly reduces V8’s overall resource consumption by using less off-thread CPU time. The energy consumption of a process can be measured easily on an M1- or M2-based Macbook using `taskinfo`. + +:::table-wrapper +| Benchmark | Energy Consumption | +| :---------: | :----------------: | +| JetStream | -3.5% | +| Speedometer | -10% | +::: + +Maglev isn’t complete by any means. We've still got plenty more work to do, more ideas to try out, and more low-hanging fruit to pick — as Maglev gets more complete, we’ll expect to see higher scores, and more reduction in energy consumption. + +Maglev is now available for desktop Chrome now, and will be rolled out to mobile devices soon.