The V8 team at Google posts about the JavaScript engine's compilers, garbage collection, WebAssembly support, and release changes.
HN links it for details on TurboFan and Ignition, such as try/catch now optimizing well, and for explanations of V8 behavior like Math.random().
This (and the older blog-post about backing stores[0]) are incredibly relevant to my LZ-string optimisations[1]. LZ-string takes an input string, builds a dictionary of substrings as it scans over the input, and outputs substring-tokens to a bit-stream, then converts that bit-stream into a new string.
How that dictionary is built is what is most relevant here: it is the greatest bottleneck in determining the speed of the algorithm. I have tried many variants. The ones that seem to work best match what is described here:
- instead of naively using sub-strings and dictionary lookup, build a trie[2] (putting all sub-strings in an object used as a flat dictionary quickly overflows the 1022-elements limit)
- instead of character strings, use charCodeAt[0] to use integer keys in the look-up of the trie (index with elements rather than hashcodes, and because JS strings are UTF16, the keys are SMI. It also removes string creation overhead).
- for a long time, I used one object for each node, with 0 as the key to look up its value, and charCode+1 to look up next nodes. In recent benchmarks it turned out that { v: val, d: {} } is faster[4][5][6]. This makes sense because: "we don't store information about present indexed properties on the HiddenClass" [0]. In other words: the new options creates a distinct hidden class compared to a plain object, which might be optimised. At the same time adding integer keys to a separate d object will not mess with this hidden class, preventing…
(This is my interpretation from reading around)
They went over it a bit at [0], but the "subtext" is that until recently, Node.js has been "following" what V8 does. When V8 makes a change, node needs to react and work with it. When Node needs to do something differently than chrome, they need to kind of figure it out on their own.
Now they are going to be treating node like they treat chrome. Working with them on new features, more integrations with debugging, better support for things that Node.js uses or new APIs to make the Node code simpler.
I'm hoping this also means less breakage between Node versions for native code.
There's also a (maybe new?) buildbot [1] which is continuously integrating V8 and Node's master branch so you can try out the newest at any time.
[0] https://v8project.blogspot.com/2016/12/v8-nodejs.html
[1] https://build.chromium.org/p/client.v8.fyi/builders/V8%20-%20node.js%20integration?numbuilds=500
That quote describes a good mental model for someone learning JavaScript, but not necessarily the physical truth.
JavaScript's numbers can do everything doubles can; but if the compiler can prove that all values that a particular variable could possibly take on will fit in a smaller type, then it can use that. So a variable really can end up stored as an integer--or never stored anywhere for that matter, like if a loop gets unrolled and the counter is optimized out.
The bitwise operators behave as if integers are 32-bit two's complement. The typed arrays are also two's complement. A smart developer can write code that a smart compiler will compile to bitwise operations on actual physical 32-bit integers.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators
https://v8project.blogspot.com/2017/09/elements-kinds-in-v8.html
In the comment section to the V8 JavaScript blog post [1], Vigna writes:
- Technically, it would be better if you used the upper
52 bits, rather than the lower 52 bits, to generate a
double. The lowest bit of a xorshift128+ generator is an
LSFR, and while people has been happy using LSFR for
decades, it is slightly inferior in quality to all other
bits. This is really OCD, as computational errors makes
the lowest bit almost irrelevant, but now you know.
I don't know whether that's been implemented, but the maintainer replied:
Thanks for the suggestions! I will definitely revisit the
current implementation with your tips in mind.
[1] https://v8project.blogspot.nl/2015/12/theres-mathrandom-and-then-theres.html?showComment=1452592903162#c1549004517443909784
Expect a blog post shortly, but from v8 we get these language changes:
- await/async behind flag (already covered in thread)
- Exponentiation operator:
> console.log(60**2*24);
86400
- Object.{values,entries}:
> o = { a: 1, b: 2}
> Object.values(o);
[ 1, 2 ]
- Object.getOwnPropertyDescriptor(s):
> o = { a: 1 }
> Object.getOwnPropertyDescriptors(o);
{ a: { value: 1, writable: true, enumerable: true, configurable: true } }
Additionally, there's a lot of pretty impressive optimization work done by the v8 team. You can read more about that on their blog: http://v8project.blogspot.com
Finally, one of my favorite things about Node.js 7.0 is the WhatWG http parser: https://github.com/nodejs/node/pull/7448.
edit: elaborated on exponentiation operator
https://bugs.chromium.org/p/v8/issues/detail?id=4698&q=tail%20calls&colspec=ID%20Type%20Status%20Priority%20Owner%20Summary%20HW%20OS%20Component%20Stars
Proper tail calls is the last ES6 feature not implemented by Chrome. Can anyone shed more light on the backstory for the hold up? I've read the challenges with debugging without a stack, but Safari's implementation of tail calls seems pretty sound and was shipped about a year ago.
The last V8 blog post on the topic is almost 18 months old:
https://v8project.blogspot.com/2016/04/es6-es7-and-beyond.html
The TC39 topic they site hasn't been touched in two years:
https://github.com/tc39/proposal-ptc-syntax