Tuesday, January 26, 2016

V8 Release 4.9

Roughly 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 Chrome branches for a Chrome Beta milestone. Today we’re pleased to announce our newest branch, V8 version 4.9, which will be in beta until it is released in coordination with Chrome 49 Stable. V8 4.9 is filled will all sorts of developer-facing goodies, so we’d like to give you a preview of some of the highlights in anticipation of the release in several weeks.

91% ECMAScript 2015 (ES6) support

In V8 release 4.9 we shipped more JavaScript ES2015 features than in any other previous release, bringing us to 91% completion as measured by the Kangax compatibility table (as of January 26). V8 now supports destructuring, default parameters, Proxy objects, and the Reflect API. Release 4.9 also makes block level constructs such as class and let available outside of strict mode and adds support for the sticky flag on regular expressions and customizable Object.prototype.toString output.

Destructuring

Variable declarations, parameters, and assignments now support destructuring of objects and arrays via patterns. For example:
let o = {a: [1, 2, 3], b: {p: 4}, c: {q: 5}};
let {a: [x, y], b: {p}, c, d} = o;            // x=1, y=2, p=4, c={q: 5}
[x, y] = [y, x];                              // x=2, y=1
function f({a, b}) { return [a, b] }
f({a: 4})                                     // [4, undefined]
Array patterns can contain rest patterns that are assigned the remainder of the array:
let [x, y, ...r] = [1, 2, 3, 4];              // x=1, y=2, r=[3,4]
Furthermore, pattern elements can be given default values, which are used in case the respective property has no match:
let {a: x, b: y = x} = {a: 4};                // x=4, y=4
let [x, y = 0, z = 0] = [1, 2];               // x=1, y=2, z=0
Destructuring can be used to make accessing data from objects and arrays more compact.

Proxies & Reflect

After years of development, V8 now ships with a complete implementation of proxies, up-to-date with the ES2015 spec. Proxies are a powerful mechanism for virtualizing objects and functions through a set of developer-provided hooks to customize property accesses. In addition to object virtualization, proxies can be used to implement interception, add validation for property setting, simplify debugging and profiling, and unlock advanced abstractions like membranes.

To proxy an object, you must create a handler placeholder object that defines various traps and apply it to the target object which the proxy virtualizes:
let target = {};
let handler = {
  get(target, name="world") {
    return `Hello, ${name}!`;
  }
};

let foo = new Proxy(target, handler);
foo.bar  // "Hello, bar!"
The Proxy object is accompanied by the Reflect module, which defines suitable defaults for all proxy traps:
let debugMe = new Proxy({}, {
  get(target, name, receiver) {
    console.log(`Debug: get called for field: ${name}`);
    return Reflect.get(target, name, receiver);
  },
  set(target, name, value, receiver) {
    console.log(`Debug: set called for field: ${name}, and value: ${value}`);
    return Reflect.set(target, name, value, receiver);
  }
});

debugMe.name = "John Doe";
// Debug: set called for field: name, and value: John Doe
let title = `Mr. ${debugMe.name}`; // "Mr. John Doe"
// Debug: get called for field: name
For more information on the usage of Proxies and the Reflect API, see the examples section of the MDN Proxy page and look out for our upcoming Proxies article on the Web Fundamentals blog.

Default Parameters

In ES5 and below, optional parameters in function definitions required boilerplate code to check whether parameters were undefined:
function sublist(list, start, end) {
  if (typeof start === "undefined") start = 0;
  if (typeof end === "undefined") end = list.length;
  ...
}
ES2015 now allows function parameters to have default values, providing for clearer and more succinct function definitions:
function sublist(list, start = 0, end = list.length) { ... }
sublist([1, 2, 3], 1)  // sublist([1, 2, 3], 1, 3)
Default parameters and destructuring can be combined, of course:
function vector([x, y, z] = []) { ... }

Classes & lexical declarations in sloppy mode

V8 has supported lexical declarations (let, const, block-local function) and classes since versions 4.1 and 4.2 respectively, but so far strict mode has been required in order to use them. As of V8 release 4.9, all of these features are now enabled outside of strict mode as well, per the ES2015 spec. This makes prototyping in the DevTools Console much easier, although we encourage developers in general to upgrade to strict mode for new code.

Regular expressions

V8 now supports the new sticky flag on regular expressions. The sticky flag toggles whether searches in strings start from the beginning of the string (normal) or from the lastIndex property (sticky). This behavior is useful for efficiently parsing arbitrarily long input strings with many different regular expressions. To enable sticky searching, add the y flag to a regex: (e.g. var regex = /foo/y; ).

Customizable Object.prototype.toString output

Using Symbol.toStringTag, user-defined types can now return customized output when passed to Object.prototype.toString (either directly or as a result of string coercion):
class Custom {
  get [Symbol.toStringTag]() {
    return "Custom"
  }
}
Object.prototype.toString.call(new Custom)  // "[object Custom]"
String(new Custom)                          // "[object Custom]"

Improved Math.random()

V8 4.9 includes an improvement in the implementation of Math.random(). As announced last month, we switched V8’s PRNG algorithm to xorshift128+ in order to provide higher-quality pseudo-randomness.

V8 API

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

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

Posted by the V8 team

Thursday, December 17, 2015

There's Math.random(), and then there's Math.random()


Math.random()
Returns a Number value with positive sign, greater than or equal to 0 but less than 1, chosen randomly or pseudo randomly with approximately uniform distribution over that range, using an implementation-dependent algorithm or strategy. This function takes no arguments.


Math.random() is the most well-known and frequently-used source of randomness in Javascript. In V8 and most other Javascript engines, it is implemented using a pseudo-random number generator (PRNG). As with all PRNGs, the random number is derived from an internal state, which is altered by a fixed algorithm for every new random number. So for a given initial state, the sequence of random numbers is deterministic. Since the bit size n of the internal state is limited, the numbers that a PRNG generates will eventually repeat themselves. The upper bound for the period length of this permutation cycle is of course 2n.

There are many different PRNG algorithms; among the most well-known ones are Mersenne-Twister and LCG. Each has its particular characteristics, advantages, and drawbacks. Ideally, it would use as little memory as possible for the initial state, be quick to perform, have a large period length, and offer a high quality random distribution. While memory usage, performance, and period length can easily be measured or calculated, the quality is harder to determine. There is a lot of math behind statistical tests to check the quality of random numbers. The de-facto standard PRNG test suite, TestU01, implements many of these tests.

Until recently (up to version 4.9.40), V8’s choice of PRNG was MWC1616 (multiply with carry, combining two 16-bit parts). It uses 64 bits of internal state and looks roughly like this:
uint32_t state0 = 1;
uint32_t state1 = 2;
uint32_t mwc1616() {
  state0 = 18030 * (state0 & 0xffff) + (state0 >> 16);
  state1 = 30903 * (state1 & 0xffff) + (state1 >> 16);
  return state0 << 16 + (state1 & 0xffff);
The 32-bit value is then turned into a floating point number between 0 and 1 in agreement with the specification.

MWC1616 uses little memory and is pretty fast to compute, but unfortunately offers sub-par quality:

  • The number of random values it can generate is limited to 232 as opposed to the 252 numbers between 0 and 1 that double precision floating point can represent.
  • The more significant upper half of the result is almost entirely dependent on the value of state0. The period length would be at most 232, but instead of few large permutation cycles, there are many short ones. With a badly chosen initial state, the cycle length could be less than 40 million.
  • It fails many statistical tests in the TestU01 suite.

This has been pointed out to us, and having understood the problem and after some research, we decided to reimplement Math.random based on an algorithm called xorshift128+. It uses 128 bits of internal state, has a period length of 2128 - 1, and passes all tests from the TestU01 suite.
uint64_t state0 = 1;
uint64_t state1 = 2;
uint64_t xorshift128plus() {
  uint64_t s1 = state0;
  uint64_t s0 = state1;
  state0 = s0;
  s1 ^= s1 << 23;
  s1 ^= s1 >> 17;
  s1 ^= s0;
  s1 ^= s0 >> 26;
  state1 = s1;
  return state0 + state1;
}
The new implementation landed in V8 4.9.41.0 within a few days of us becoming aware of the issue. It will become available with Chrome 49. Both Firefox and Safari switched to xorshift128+ as well.

Make no mistake however: even though xorshift128+ is a huge improvement over MWC1616, it still is not cryptographically secure. For use cases such as hashing, signature generation, and encryption/decryption, ordinary PRNGs are unsuitable. The Web Cryptography API introduces window.crypto.getRandomValues, a method that returns cryptographically secure random values, at a performance cost.

Please keep in mind, if you find areas of improvement in V8 and Chrome, even ones that—like this one—do not directly affect spec compliance, stability, or security, please file an issue on our bug tracker.

Posted by Yang Guo, Software Engineer and dice designer

Wednesday, November 25, 2015

V8 Release 4.8

Roughly 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 Chrome branches for a Chrome Beta milestone. Today we’re pleased to announce our newest branch, V8 version 4.8, which will be in beta until it is released in coordination with Chrome 48 Stable. V8 4.8 contains a handful of developer-facing features, so we’d like to give you a preview of some of the highlights in anticipation of the release in several weeks.

Improved ECMAScript 2015 (ES6) support

This release of V8 provides support for two well-known symbols, built-in symbols from the ES2015 spec that allow developers to leverage several low-level language constructs which were previously hidden.

@@isConcatSpreadable

The name for a Boolean valued property that if true indicates an object should be flattened to its array elements by Array.prototype.concat.

(function(){
"use strict";
class AutomaticallySpreadingArray extends Array {
   get [Symbol.isConcatSpreadable]() {
      return true;
   }
}
let first = [1];
let second = new AutomaticallySpreadingArray();
second[0] = 2;
second[1] = 3;
let all = first.concat(second);
// Outputs [1, 2, 3]
console.log(all);
}());

@@toPrimitive

The name for a method to invoke on an object for implicit conversions to primitive values.

(function(){
"use strict";
class V8 {
   [Symbol.toPrimitive](hint) {
      if (hint === 'string') {
         console.log('string');
         return 'V8';
      } else if (hint === 'number') {
         console.log('number');
         return 8;
      } else {
         console.log('default:' + hint);
         return 8;
      }
   }
}

let engine = new V8();
console.log(Number(engine));
console.log(String(engine));
}());

ToLength

The ES2015 spec adjusts the abstract operation for type conversion to convert an argument to an integer suitable for use as the length of an array-like object.  (While not directly observable, this change might be indirectly visible when dealing with array-like objects with negative length.)

V8 API

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

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

Posted by the V8 team

Friday, October 30, 2015

Jank Busters Part One

Jank, or in other words visible stutters, can be noticed when Chrome fails to render a frame within 16.66ms (disrupting 60 frames per second motion). As of today most of the V8 garbage collection work is performed on the main rendering thread, c.f. Figure 1, often resulting in jank when too many objects need to be maintained. Eliminating jank has always been a high priority for the V8 team [1, 2, 3]. In this blog post we will discuss a few optimizations that were implemented between M41 and M46 which significantly reduce garbage collection pauses resulting in better user experience.

Figure 1: Garbage collection performed on the main thread.

A major source of jank during garbage collection is processing various bookkeeping data structures. Many of these data structures enable optimizations that are unrelated to garbage collection. Two examples are the list of all ArrayBuffers, and each ArrayBuffer’s list of views. These lists allow for an efficient implementation of the DetachArrayBuffer operation without imposing any performance hit on access to an ArrayBuffer view. In situations, however, where a web page creates millions of ArrayBuffers, (e.g., WebGL-based games), updating those lists during garbage collection causes significant jank. In M46, we removed these lists and instead detect detached buffers by inserting checks before every load and store to ArrayBuffers. This amortizes the cost of walking the big bookkeeping list during GC by spreading it throughout program execution resulting in less jank. Although the per-access checks can theoretically slow down the throughput of programs that heavily use ArrayBuffers, in practice V8's optimizing compiler can often remove redundant checks and hoist remaining checks out of loops, resulting in a much smoother execution profile with little or no overall performance penalty.

Another source of jank is the bookkeeping associated with tracking the lifetimes of objects shared between Chrome and V8. Although the Chrome and V8 memory heaps are distinct, they must be synchronized for certain objects, like DOM nodes, that are implemented in Chrome's C++ code but accessible from JavaScript. V8 creates an opaque data type called a handle that allows Chrome to manipulate a V8 heap object without knowing any of the details of the implementation. The object's lifetime is bound to the handle: as long as Chrome keeps the handle around, V8's garbage collector won't throw away the object. V8 creates an internal data structure called a global reference for each handle it passes back out to Chrome through the V8 API, and these global references are what tell V8's garbage collector that the object is still alive. For WebGL games, Chrome may create millions of such handles, and V8, in turn, needs to create the corresponding global references to manage their lifecycle. Processing these huge amounts of global references in the main garbage collection pause is observable as jank. Fortunately, objects communicated to WebGL are often just passed along and never actually modified, enabling simple static escape analysis. In essence, for WebGL functions that are known to usually take small arrays as parameters the underlying data is copied on the stack, making a global reference obsolete. The result of such a mixed approach is a reduction of pause time by up to 50% for rendering-heavy WebGL games.

Most of V8’s garbage collection is performed on the main rendering thread. Moving garbage collection operations to concurrent threads reduces the waiting time for the garbage collector and further reduces jank. This is an inherently complicated task since the main JavaScript application and the garbage collector may simultaneous observe and modify the same objects. Until now, concurrency was limited to sweeping the old generation of the regular object JS heap. Recently, we also implemented concurrent sweeping of the code and map space of the V8 heap. Additionally, we implemented concurrent unmapping of unused pages to reduce the work that has to be performed on the main thread, c.f. Figure 2.

Figure 2: Some garbage collection operations performed on the concurrent garbage collection threads.

The impact of the discussed optimizations is clearly visible in WebGL-based games, for example Turbolenz' Oort Online demo. The following video compares Chrome M41 to M46


We are currently in the process of making more garbage collection components incremental, concurrent, and parallel, to shrink garbage collection pause times on the main thread even further. Stay tuned as we have some interesting patches in the pipeline.

Posted by the jank busters: Jochen Eisinger, Michael Lippautz, and Hannes Payer

Wednesday, October 14, 2015

V8 Release 4.7

Roughly 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 Chrome branches for a Chrome Beta milestone. Today we’re pleased to announce our newest branch, V8 version 4.7, which will be in beta until it is released in coordination with Chrome 47 Stable. V8 4.7 is filled will all sorts of developer-facing goodies, so we’d like to give you a preview of some of the highlights in anticipation of the release in several weeks.

Improved ECMAScript 2015 (ES6) support

Rest operator

The Rest operator enables the developer to pass an indefinite number of arguments to a function. It is similar to the arguments object.

//Without Rest operator
function concat() {
 var args = Array.prototype.slice.call(arguments, 1);
 return args.join("");
} 

//With Rest operator
function concatWithRest(...strings) {
 return strings.join("");
}

Support for upcoming ES features

Array.prototype.includes

The new Array.prototype.includes method is a new feature that is currently a stage 3 proposal for inclusion in ES2016. It provides a terse syntax for determining whether or not an element is in a given array by returning a boolean value.

[1, 2, 3].includes(3) // true
["apple", "banana", "cherry"].includes("apple") // true
["apple", "banana", "cherry"].includes("peach") // false

Ease the pressure on memory while parsing

Recent changes to the V8 parser greatly reduce the memory consumed by parsing files with large nested functions. In particular, this allows V8 to run larger asm.js modules than previously possible.

V8 API

Please check out our summary of API changes. This document gets regularly updated a few weeks after each major release. Developers with an active V8 checkout can use 'git checkout -b 4.7 -t branch-heads/4.7' to experiment with the new features in V8 4.7. Alternatively you can subscribe to Chrome's Beta channel and try the new features out yourself soon.

Posted by the V8 team

Friday, September 25, 2015

Custom startup snapshots

The JavaScript specification includes a lot of built-in functionality, from math functions to a full-featured regular expression engine. Every newly-created V8 context has these functions available from the start. For this to work, the global object (for example, the window object in a browser) and all the built-in functionality must be set up and initialized into V8’s heap at the time the context is created. It takes quite some time to do this from scratch.

Fortunately, V8 uses a shortcut to speed things up: just like thawing a frozen pizza for a quick dinner, we deserialize a previously-prepared snapshot directly into the heap to get an initialized context. On a regular desktop computer, this can bring the time to create a context from 40 ms down to less than 2 ms. On an average mobile phone, this could mean a difference between 270 ms and 10 ms.

Applications other than Chrome that embed V8 may require more than vanilla Javascript. Many load additional library scripts at startup, before the “actual” application runs. For example, a simple TypeScript VM based on V8 would have to load the TypeScript compiler on startup in order to translate TypeScript source code into JavaScript on-the-fly.

As of the release of V8 4.3 two months ago, embedders can utilize snapshotting to skip over the startup time incurred by such an initialization.. The test case for this feature shows how this API works.

To create a snapshot, we can call v8::V8::CreateSnapshotDataBlob with the to-be-embedded script as a null-terminated C string. After creating a new context, this script is compiled and executed. In our example, we create two custom startup snapshots, each of which define functions on top of what JavaScript already has built in.

We can then use v8::Isolate::CreateParams to configure a newly-created isolate so that it initializes contexts from a custom startup snapshot. Contexts created in that isolate are exact copies of the one from which we took a snapshot. The functions defined in the snapshot are available without having to define them again.

There is an important limitation to this: the snapshot can only capture V8’s heap. Any interaction from V8 with the outside is off-limits when creating the snapshot.  Such interactions include:

  • defining and calling API callbacks (i.e. functions created via v8::FunctionTemplate)
  • creating typed arrays, since the backing store may be allocated outside of V8
And of course, values derived from sources such as Math.random or Date.now are fixed once the snapshot has been captured. They are no longer really random nor reflect the current time.

Limitations aside, startup snapshots remain a great way to save time on initialization. We can shave off 100ms from the startup spent on loading the TypeScript compiler in our example above (on a regular desktop computer). We're looking forward to seeing how you might put custom snapshots to use!

Posted by Yang Guo, Software Engineer and engine pre-heater supplier

Friday, August 28, 2015

V8 Release 4.6

Roughly 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 Chrome branches for a Chrome Beta milestone. Today we’re pleased to announce our newest branch, V8 version 4.6, which will be in beta until it is released in coordination with Chrome 46 Stable. V8 4.6 is filled will all sorts of developer-facing goodies, so we’d like to give you a preview of some of the highlights in anticipation of the release in several weeks.

Improved ECMAScript 2015 (ES6) support

V8 4.6 adds support for several ECMAScript 2015 (ES6) features.

Spread operator

The spread operator makes it much more convenient to work with arrays. For example it makes imperative code obsolete when you simply want to merge arrays.

// Merging arrays
// Code without spread operator
let inner = [3, 4];
let merged = [0, 1, 2].concat(inner, [5]);

// Code with spread operator
let inner = [3, 4];
let merged = [0, 1, 2, ...inner, 5];
Another good use of the spread operator to replace apply().

// Function parameters stored in an array
// Code without spread operator
function myFunction(a, b, c) {
   console.log(a);
   console.log(b);
   console.log(c);
}
let argsInArray = ["Hi ", "Spread ", "operator!"];
myFunction.apply(null, argsInArray);

// Code with spread operator
function myFunction (a,b,c) {
   console.log(a);
   console.log(b);
   console.log(c);
}

let argsInArray = ["Hi ", "Spread ", "operator!"];
myFunction(...argsInArray);

new.target

new.target is one of ES6's features designed to improve working with classes. Under the hood it’s actually an implicit parameter to every function. If a function is called with the keyword new, then the parameter holds a reference to the called function. If new is not used the parameter is undefined.

In practice, this means that you can use new.target to figure out whether a function was called normally or constructor-called via the new keyword.

function myFunction() {
   if (new.target === undefined) {
      throw "Try out calling it with new.";
   }
   console.log("Works;");
}

// Will break
myFunction();

// Will work
let a = new myFunction();
When ES6 classes and inheritance are used, new.target inside the constructor of a super-class is bound to the derived constructor that was invoked with new. In particular, this gives super-classes access to the prototype of the derived class during construction.

Reduce the jank

Jank can be a pain, especially when playing a game. Often, it's even worse when the game features multiple players. oortonline.gl is a WebGL benchmark that tests the limits of current browsers by rendering a complex 3D scene with particle effects and modern shader rendering. The V8 team set off in a quest to push the limits of Chrome’s performance in these environments. We’re not done yet, but the fruits of our efforts are already paying off. Chrome 46 shows incredible advances in oortonline.gl performance which you can see yourself below.



Some of the optimizations include:

The good thing is that all changes related to oortonline.gl are general improvements that potentially affect all users of applications that make heavy use of WebGL.

V8 API

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

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


Posted by the V8 team