Tuesday, July 18, 2017

Upcoming RegExp Features

Regular expressions, or RegExps, are an important part of the JavaScript language. When used properly, they can greatly simplify string processing.

ES2015 introduced many new features to the JavaScript language, including significant improvements to the regular expression syntax with the Unicode (/u) and sticky (/y) flags. But development has not stopped since then — in tight collaboration with other members at TC39 (the ECMAScript standards body), the V8 team has proposed and co-designed several new features to make RegExps even more powerful.

These features are currently being proposed for inclusion in the JavaScript specification. Even though the proposals have not been fully accepted, they are already at Stage 3 in the TC39 process. We have implemented these features behind flags (see below) in order to be able to provide timely design and implementation feedback before the specification is finalized.

In this blog post we want to give you a preview of this exciting future. If you'd like to follow along with the upcoming examples, enable experimental JavaScript features at chrome://flags/#enable-javascript-harmony.

Named Captures

Regular expressions can contain so-called captures (or groups), which can capture a portion of the matched text. So far, developers could only refer to these captures by their numeric index, which is determined by the position of the capture within the pattern.
const pattern = /(\d{4})-(\d{2})-(\d{2})/u;
const result = pattern.exec('2017-07-03');
// result[0] === '2017-07-03'
// result[1] === '2017'
// result[2] === '07'
// result[3] === '03'

But regular expressions are already notoriously difficult to read, write, and maintain, and numeric references can add further complications. For instance, in longer patterns it can be tricky to determine the index of a particular capture:
/(?:(.)(.(?<=[^(])(.)))/  // Index of the last capture?

And even worse, changes to a pattern can potentially shift the indices of all existing captures:
/(a)(b)(c)\3\2\1/     // A few simple numbered backreferences.
/(.)(a)(b)(c)\4\3\2/  // All need to be updated.

Named captures are an upcoming feature that helps mitigate these issues by allowing developers to assign names to captures. The syntax is similar to Perl, Java, .Net, and Ruby:
const pattern = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
const result = pattern.exec('2017-07-03');
// result.groups.year === '2017'
// result.groups.month === '07'
// result.groups.day === '03'

Named captures can also be referenced by named backreferences and through String.prototype.replace:
// Named backreferences.
/(?<lowercasex>x)y\k<lowercasex>/.test('xyx');  // true

// String replacement.
const pattern = /(?<fst>a)(?<snd>b)/;
'ab'.replace(pattern, '$<snd>$<fst>');  // 'ba'
'ab'.replace(pattern, (m, p1, p2, o, s, {fst, snd}) => fst + snd);  // 'ba'

Full details of this new feature are available in the specification proposal.

dotAll Flag

By default, the . atom in regular expressions matches any character except for line terminators:
/foo.bar/u.test('foo\nbar');  // false

A proposal introduces dotAll mode, enabled through the /s flag. In dotAll mode, . matches line terminators as well.
/foo.bar/su.test('foo\nbar');  // true

Full details of this new feature are available in the specification proposal.

Unicode Property Escapes

Regular expression syntax has always included shorthands for certain character classes. \d represents digits and is really just [0-9]; \w is short for word characters, or [A-Za-z0-9_].

With Unicode awareness introduced in ES2015, there are suddenly many more characters that could be considered numbers, for example the circled digit one: ①; or considered word characters, for example the Chinese character for snow: 雪.

Neither of these can be matched with \d or \w. Changing the meaning of these shorthands would break existing regular expression patterns.

Instead, new character classes are being introduced. Note that they are only available for Unicode-aware RegExps denoted by the /u flag.
/\p{Number}/u.test('①');     // true
/\p{Alphabetic}/u.test('雪');  // true

The inverse can be matched by with \P.
/\P{Number}/u.test('①');     // false
/\P{Alphabetic}/u.test('雪');  // false

The Unicode consortium defines many more ways to classify code points, for example math symbols or Japanese Hiragana characters:
/^\p{Math}+$/u.test('∛∞∉');                            // true
/^\p{Script_Extensions=Hiragana}+$/u.test('ひらがな');  // true

The full list of supported Unicode property classes can be found in the current specification proposal. For more examples, take a look at this informative article.

Lookbehind Assertions

Lookahead assertions have been part of JavaScript’s regular expression syntax from the start. Their counterpart, lookbehind assertions, are finally being introduced. Some of you may remember that this has been part of V8 for quite some time already. We even use lookbehind asserts under the hood to implement the Unicode flag specified in ES2015.

The name already describes its meaning pretty well. It offers a way to restrict a pattern to only match if preceded by the pattern in the lookbehind group. It comes in both matching and non-matching flavors:
/(?<=\$)\d+/.exec('$1 is worth about ¥123');  // ['1']
/(?<!\$)\d+/.exec('$1 is worth about ¥123');  // ['123']

For more details, check out our previous blog post dedicated to lookbehind assertions, and examples in related V8 test cases.


This blog post wouldn’t be complete without mentioning some of the people that have worked hard to make this happen: especially language champions Mathias Bynens, Dan Ehrenberg, Claude Pache, Brian Terlson, Thomas Wood, Gorkem Yakin, and Irregexp guru Erik Corry; but also everyone else who has contributed to the language specification and V8’s implementation of these features.

We hope you’re as excited about these new RegExp features as we are!

Friday, June 9, 2017

V8 Release 6.0

Every six weeks, we create a new branch of V8 as part of our release process. Each version is branched from V8’s git master immediately before a Chrome Beta milestone. Today we’re pleased to announce our newest branch, V8 version 6.0, which will be in beta until it is released in coordination with Chrome 60 Stable in several weeks. V8 6.0 is filled with all sorts of developer-facing goodies. We’d like to give you a preview of some of the highlights in anticipation of the release.


V8 6.0 introduces support for SharedArrayBuffer, a low-level mechanism to share memory between JavaScript workers and synchronize control flow across workers. SharedArrayBuffers give JavaScript access to shared memory, atomics, and futexes. SharedArrayBuffers also unlock the ability to port threaded applications to the web via asm.js or WebAssembly.

For a brief, low-level tutorial, see the spec tutorial page or consult the Emscripten documentation for porting pthreads.

Object rest/spread properties

This release introduces rest properties for object destructuring assignment and spread properties for object literals. Object rest/spread properties are Stage 3 ES.next features.

Spread properties also offer a terse alternative to Object.assign() in many situations.

// Rest properties for object destructuring assignment:
const person = {
  firstName: 'Sebastian',
  lastName: 'Markbåge',
  country: 'USA',
  state: 'CA',
const { firstName, lastName, ...rest } = person;
console.log(firstName); // Sebastian
console.log(lastName); // Markbåge
console.log(rest); // { country: 'USA', state: 'CA' }

// Spread properties for object literals:
const personCopy = { firstName, lastName, ...rest };
// { firstName: 'Sebastian', lastName: 'Markbåge', country: 'USA', state: 'CA' }

For more information, see the usage post on Web Fundamentals.

ES6 Performance

V8 6.0 continues to improve performance of ES2015 features. This release contains optimizations to language feature implementations that overall result in a roughly 10% improvement in V8’s ARES-6 score.


Please check out our summary of API changes. This document is regularly updated a few weeks after each major release.

Developers with an active V8 checkout can use 'git checkout -b 6.0 -t branch-heads/6.0' to experiment with the new features in V8 6.0. Alternatively you can subscribe to Chrome's Beta channel and try the new features out yourself soon.

Posted by the V8 team

Monday, May 15, 2017

Launching Ignition and TurboFan

Today we are excited to announce the launch of a new JavaScript execution pipeline for V8 5.9 that will reach Chrome Stable in M59. With the new pipeline, we achieve big performance improvements and significant memory savings on real-world JavaScript applications. We’ll discuss the numbers in more detail at the end of this post, but first let’s take a look at the pipeline itself.

The new pipeline is built upon Ignition, V8’s interpreter, and TurboFan, V8’s newest optimizing compiler. These technologies should be familiar to those of you who have followed the V8 blog over the last few years, but the switch to the new pipeline marks a big new milestone for both.

For the first time, Ignition and TurboFan are used universally and exclusively for JavaScript execution in V8 5.9. Furthermore, starting with 5.9, Full-codegen and Crankshaft, the technologies that served V8 well since 2010, are no longer used in V8 for JavaScript execution, since they no longer are able to keep pace with new JavaScript language features and the optimizations those features require. We plan to remove them completely very soon. That means that V8 will have an overall much simpler and more maintainable architecture going forward.

A Long Journey

The combined Ignition and TurboFan pipeline has been in development for almost 3½ years. It represents the culmination of the collective insight that the V8 team has gleaned from measuring real-world JavaScript performance and carefully considering the shortcomings of Full-codegen and Crankshaft. It is a foundation with which we will be able to continue to optimize the entirety of the JavaScript language for years to come.

The TurboFan project originally started in late 2013 to address the shortcomings of Crankshaft. Crankshaft can only optimize a subset of the JavaScript language. For example, it was not designed to optimize JavaScript code using structured exception handling, i.e. code blocks demarcated by JavaScript’s try, catch, and finally keywords. It is difficult to add support for new language features in Crankshaft, since these features almost always require writing architecture-specific code for nine supported platforms. Furthermore, Crankshaft’s architecture is limited in the extent that it can generate optimal machine code. It can only squeeze so much performance out of JavaScript, despite requiring the V8 team to maintain more than ten thousand lines of code per chip architecture.

TurboFan was designed from the beginning not only to optimize all of the language features found in the JavaScript standard at the time, ES5, but also all the future features planned for ES2015 and beyond. It introduces a layered compiler design that enables a clean separation between high-level and low-level compiler optimizations, making it easy to add new language features without modifying architecture-specific code. TurboFan adds an explicit instruction selection compilation phase that makes it possible to write far less architecture-specific code for each supported platform in the first place. With this new phase, architecture-specific code is written once and it rarely needs to be changed. These and other decisions lead to a more maintainable and extensible optimizing compiler for all of the architectures that V8 supports.

The original motivation behind V8’s Ignition interpreter was to reduce memory consumption on mobile devices. Before Ignition, the code generated by V8’s Full-codegen baseline compiler typically occupied almost one third of the overall JavaScript heap in Chrome. That left less space for a web application’s actual data. When Ignition was enabled for Chrome M53 on Android devices with limited RAM, the memory footprint required for baseline, non-optimized JavaScript code shrank by a factor of nine on ARM64-based mobile devices.

Later the V8 team took advantage of the fact that Ignition’s bytecode can be used to generate optimized machine code with TurboFan directly rather than having to re-compile from source code as Crankshaft did. Ignition’s bytecode provides a cleaner and less error-prone baseline execution model in V8, simplifying the deoptimization mechanism that is a key feature of V8’s adaptive optimization. Finally, since generating bytecode is faster than generating Full-codegen’s baseline compiled code, activating Ignition generally improves script startup times and in turn, web page loads.

By coupling the design of Ignition and TurboFan closely, there are even more benefits to the overall architecture. For example, rather than writing Ignition’s high-performance bytecode handlers in hand-coded assembly, the V8 team instead uses TurboFan’s intermediate representation to express the handlers’ functionality and lets TurboFan do the optimization and final code generation for V8’s numerous supported platforms. This ensures Ignition performs well on all of V8’s supported chip architectures while simultaneously eliminating the burden of maintaining nine separate platform ports.

Running the Numbers

History aside, now let’s take a look at the new pipeline’s real-world performance and memory consumption.

The V8 team continually monitors the performance of real-world use cases using the Telemetry - Catapult framework. Previously in this blog we’ve discussed why it’s so important to use the data from real-world tests to drive our performance optimization work and how we use WebPageReplay together with Telemetry to do so. The switch to Ignition and TurboFan shows performance improvements in those real-world test cases. Specifically, the new pipeline results in significant speed-ups on user interaction story tests for well-known websites:

Reduction in time spent in V8 for user interaction benchmarks

Although Speedometer is a synthetic benchmark, we’ve previously uncovered that it does a better job of approximating the real-world workloads of modern JavaScript than other synthetic benchmarks. The switch to Ignition and TurboFan improves V8’s Speedometer score by 5%-10%, depending on platform and device.

The new pipeline also speeds up server-side JavaScript. AcmeAir, a benchmark for Node.js that simulates the server backend implementation of a fictitious airline, runs more than 10% faster using V8 5.9.

Improvements on Web and Node.js benchmarks

Ignition and TurboFan also reduce V8’s overall memory footprint. In Chrome M59, the new pipeline slims V8’s memory footprint on desktop and high-end mobile devices by 5-10%. This reduction is a result of bringing the Ignition memory savings that have been previously covered in this blog to all devices and platforms supported by V8.

These improvements are just the start. The new Ignition and TurboFan pipeline paves the way for further optimizations that will boost JavaScript performance and shrink V8’s footprint in both Chrome and in Node.js for years to come. We look forward to sharing those improvements with you as we roll them out to developers and users. Stay tuned.

Posted by the V8 team

Wednesday, May 3, 2017

Energizing Atom with V8's custom start-up snapshot

The team behind Atom, a text editor based on the Electron framework, recently published an article detailing significant improvements to start-up time, owing big parts of the gains to the usage of V8's custom start-up snapshot. Awesome!

Native Electron apps, including Atom, leverage Chromium to display a GUI and Node.js as an execution environment, both of which respectively embed V8 to run JavaScript. This allows Electron apps to take advantage of V8 snapshots to quickly initialize a previously serialized heap for faster startup. Electron developers have even released electron-link, a convenience library for setting up this feature, which Atom heavily relies on for its performance optimizations.

We announced V8's support for custom start-up snapshot more than a year ago. With v8::V8::CreateSnapshotDataBlob, embedders can provide an additional script to customize a start-up snapshot. New contexts created from this snapshot are initialized as if the additional script has already been executed. As the Atom team has shown, using a custom start-up snapshot can significantly boost start-up performance.

To quote the Atom article:
The tricky part of using this technology, however, is that the code is executed in a bare V8 context. In other words, it only allows us to run plain JavaScript code and does not provide access to native modules, Node/Electron APIs or DOM manipulation.
To work around this restriction, electron-link goes great lengths to make sure native functions (backed by C++ functions) are not included in the snapshot, and are instead loaded lazily. V8's serializer simply does not know how to serialize these native functions. Instead, native functions are wrapped into helper functions that load them lazily at runtime.

Since our original post about snapshots, the V8 team has continued developing the snapshot API and added many features and improvements. These features include support for serializing native functions. Provided that the backing C++ functions have been registered with V8, the serializer can now recognize and encode native functions for the deserializer to restore later. We’re happy to announce that the work-around in electron-link is no longer necessary.

One caveat remains: the serializer cannot directly capture state outside of V8, for example changes to the DOM in case of Atom. However, outside state directly attached to JavaScript objects via embedder fields (previously named "internal fields") can now be serialized and deserialized through a new callback API. With some work, this feature allows outside state to be put into the snapshot after all.

Some other highlights include:

  • FunctionTemplate and ObjectTemplate objects can now be added and extracted from the snapshot, so that they do not have to be set up from scratch.
  • V8 can now run multiple scripts or apply any other modifications to the context before creating the snapshot, as opposed to only a single source string.
  • Multiple differently-configured contexts can now be included in the same start-up snapshot blob.

To make these features more accessible, we designed a new, more powerful API with v8::SnapshotCreator. The old API is now merely a wrapper around this underlying API. This is how v8::V8::CreateSnapshotDataBlob is implemented:
StartupData V8::CreateSnapshotDataBlob(const char* embedded_source) {
  StartupData result = {nullptr, 0};
    SnapshotCreator snapshot_creator;
    // Obtain an isolate provided by SnapshotCreator.
    Isolate* isolate = snapshot_creator.GetIsolate();
      HandleScope scope(isolate);
      // Create a new context and optionally run some script.
      Local context = Context::New(isolate);
      if (embedded_source != NULL &&
          !RunExtraCode(isolate, context, embedded_source, "")) {
        return result;
      // Add the possibly customized context to the SnapshotCreator.
    // Use the SnapshotCreator to create the snapshot blob.
    result = snapshot_creator.CreateBlob(
  return result;

For more advanced code examples, take a look at these test cases:

The new API is available in V8 version 5.7 and later. We hope that these new features will help embedders make even better use of custom start-up snapshot. If you have any questions, please reach out to our v8-users mailing list.

Posted by Yang Guo

Thursday, April 27, 2017

V8 Release 5.9

Every six weeks, we create a new branch of V8 as part of our release process. Each version is branched from V8’s git master immediately before a Chrome Beta milestone. Today we’re pleased to announce our newest branch, V8 version 5.9, which will be in beta until it is released in coordination with Chrome 59 Stable in several weeks. V8 5.9 is filled with all sorts of developer-facing goodies. We’d like to give you a preview of some of the highlights in anticipation of the release.

Ignition+Turbofan launched

V8 5.9 is going to be the first version with Ignition+Turbofan enabled by default. In general, this switch should lead to lower memory consumption and faster startup for web application across the board, and we don’t expect stability or performance issues because the new pipeline has already undergone significant testing. However, give us a call in case your code suddenly starts to significantly regress in performance.

A dedicated blog post will delve deeper into this topic soon.

WebAssembly TrapIf support on all platforms

The TrapIf support significantly reduced the time spent compiling code (~30 %).


Please check out our summary of API changes. This document is regularly updated a few weeks after each major release.

Developers with an active V8 checkout can use 'git checkout -b 5.9 -t branch-heads/5.9' to experiment with the new features in V8 5.9. Alternatively you can subscribe to Chrome's Beta channel and try the new features out yourself soon.

Posted by the V8 team

Wednesday, April 12, 2017

Retiring Octane

The genesis of Octane

The history of JavaScript benchmarks is a story of constant evolution. As the web expanded from simple documents to dynamic client-side applications, new JavaScript benchmarks were created to measure workloads that became important for new use cases. This constant change has given individual benchmarks finite lifespans. As web browser and virtual machine (VM) implementations begin to over-optimize for specific test cases, benchmarks themselves cease to become effective proxies for their original use cases. One of the first JavaScript benchmarks, SunSpider, provided early incentives for shipping fast optimizing compilers. However, as VM engineers uncovered the limitations of microbenchmarks and found new ways to optimize around SunSpider’s limitations, the browser community retired SunSpider as a recommended benchmark.

Designed to mitigate some of the weaknesses of early microbenchmarks, the Octane benchmark suite was first released in 2012. It evolved from an earlier set of simple V8 test cases and became a common benchmark for general web performance. Octane consists of 17 different tests, which were designed to cover a variety of different workloads, ranging from Martin Richards’ kernel simulation test to a version of Microsoft’s TypeScript compiler compiling itself. The contents of Octane represented the prevailing wisdom around measuring JavaScript performance at the time of its creation.

Diminishing returns and over-optimization

In the first few years after its release, Octane provided a unique value to the JavaScript VM ecosystem. It allowed engines, including V8, to optimize their performance for a class of applications that stressed peak performance. These CPU-intensive workloads were initially underserviced by VM implementations. Octane helped engine developers deliver optimizations that allowed computationally-heavy applications to reach speeds that made JavaScript a viable alternative to C++ or Java. In addition, Octane drove improvements in garbage collection which helped web browsers avoid long or unpredictable pauses.

By 2015, however, most JavaScript implementations had implemented the compiler optimizations needed to achieve high scores on Octane. Striving for even higher benchmark scores on Octane translated into increasingly-marginal improvements in the performance of real web pages. Investigations into the execution profile of running Octane versus loading common websites (such as Facebook, Twitter, or Wikipedia) revealed that the benchmark doesn’t exercise V8’s parser or the browser loading stack the way real-world code does. Moreover, the style of Octane’s JavaScript doesn’t match the idioms and patterns employed by most modern frameworks and libraries (not to mention transpiled code or newer ES2015+ language features). This means that using Octane to measure V8 performance didn’t capture important use cases for the modern web, such as loading frameworks quickly, supporting large applications with new patterns of state management, or ensuring that ES2015+ features are as fast as their ES5 equivalents.

In addition, we began to notice that JavaScript optimizations which eked out higher Octane scores often had a detrimental effect on real-world scenarios. Octane encourages aggressive inlining to minimize the overhead of function calls, but inlining strategies that are tailored to Octane have led to regressions from increased compilation costs and higher memory usage in real-world use cases. Even when an optimization may be genuinely useful in the real-world, as is the case with dynamic pretenuring, chasing higher Octane scores can result in developing overly-specific heuristics which have little effect or even degrade performance in more generic cases. We found that Octane-derived pretenuring heuristics led to performance degradations in modern frameworks such as Ember. The `instanceof` operator was another example of an optimization tailored to a narrow set of Octane-specific cases that led to significant regressions in Node.js applications.

Another problem is that over time, small bugs in Octane become a target for optimizations themselves. For example, in the Box2DWeb benchmark, taking advantage of a bug where two objects were compared using the `<` and `>=` operators gave a ~15% performance boost on Octane. Unfortunately, this optimization had no effect in the real world and complicates more general types of comparison optimizations. Octane sometimes even negatively penalizes real-world optimizations: engineers working on other VMs have noticed that Octane seems to penalize lazy parsing, a technique that helps most real websites load faster given the amount of dead code frequently found in the wild.

Beyond Octane and other synthetic benchmarks

These examples are just some of the many optimizations which increased Octane scores to the detriment of running real websites. Unfortunately, similar issues exist in other static or synthetic benchmarks, including Kraken and JetStream. Simply put, such benchmarks are insufficient methods of measuring real-world speed and create incentives for VM engineers to over-optimize narrow use cases and under-optimize generic cases, slowing down JavaScript code in the wild.

Given the plateau in scores across most JS VMs and the increasing conflict between optimizing for specific Octane benchmarks rather than implementing speedups for a broader range of real-world code, we believe that it is time to retire Octane as a recommended benchmark.

Octane enabled the JS ecosystem to make large gains in computationally-expensive JavaScript. The next frontier, however, is improving the performance of real web pages, modern libraries, frameworks, ES2015+ language features, new patterns of state management, immutable object allocation, and module bundling. Since V8 runs in many environments, including server side in Node.js, we are also investing time in understanding real-world Node applications and measuring server-side JavaScript performance through workloads such as AcmeAir.

Check back here for more posts about improvements in our measurement methodology and new workloads that better represent real-world performance. We are excited to continue pursuing the performance that matters most to users and developers!

Posted by the V8 team

Monday, March 20, 2017

V8 Release 5.8

Every six weeks, we create a new branch of V8 as part of our release process. Each version is branched from V8’s git master immediately before a Chrome Beta milestone. Today we’re pleased to announce our newest branch, V8 version 5.8, which will be in beta until it is released in coordination with Chrome 58 Stable in several weeks. V8 5.8 is filled with all sorts of developer-facing goodies. We’d like to give you a preview of some of the highlights in anticipation of the release.

Arbitrary heap sizes

Historically the V8 heap limit was conveniently set to fit the signed 32-bit integer range with some margin. Over time this convenience lead to sloppy code in V8 that mixed types of different bit widths, effectively breaking the ability to increase the limit. In 5.8 we enabled the use of arbitrary heap sizes. See the dedicated blog post for more information.

Startup performance

In 5.8 the work towards incrementally reducing the time spent in V8 during startup was continued. Reductions in the time spent compiling and parsing code, as well as optimizations in the IC system yielded ~5 % improvements on our real-world startup workloads.


Please check out our summary of API changes. This document is regularly updated a few weeks after each major release.

Developers with an active V8 checkout can use 'git checkout -b 5.8 -t branch-heads/5.8' to experiment with the new features in V8 5.8. Alternatively you can subscribe to Chrome's Beta channel and try the new features out yourself soon.

Posted by the V8 team