
Optimizing hash tables: hiding the hash code - signa11
https://v8project.blogspot.com/2018/01/hash-code.html
======
Twirrim
> In V8, the hash code is just a random number, independent of the object
> value. Therefore, we can’t recompute it, meaning we must store it.

That's the most interesting point in the entire article, and there's no
explanation associated. Why are they using random numbers for the hash code?

~~~
blauditore
Is it even still a hash code/table in that case, technically? I thought the
whole point of a hash table was the hash function "randomly" but reproducably
assigning a position to items. This way, it's more some indexed storage with
randomized positions.

~~~
Tarean
Can't use the object content since that can change. So you need a unique
identifier.

Memory position can't be used because the gc moves objects around.

Random number it is.

~~~
danbruc
_Can 't use the object content since that can change._

It is not uncommon to use the state of an object to derive the hash code. It
of course puts the burden to ensure that objects do not change in undesired
ways while they are required to yield a stable hash code, for example while
used as a key in a hash table, onto the developer. One common solution is to
restrict the hash code calculation to immutable parts of the state, for
example an ID.

The advantage of deriving the hash code from the state is that you can use the
hash code to speed up object equality tests, i.e. if the hash codes of two
objects do not match, they can not be equal.

------
vanderZwan
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 de-opts.

\- for _really_ fast performance except in a few degenerate cases, instead of
key look-up, use an array with linear search (yes, really! Turns out compact
arrays without holes are faster to the point where even linear search makes it
worth it - up to a certain length).

So this explains a lot of _why_ certain approaches work better than others.
Some of the explanations here might let me optimise it even further, but at
this point it would become hyper-optimized for V8. Are there some take-aways
that I can generalise? And is there a similar blog for the other big
javascript engines?

I expect that "type stability" \- that is, maintaining identical hidden
classes - is fairly universal for example.

[0] [https://v8project.blogspot.se/2017/08/fast-
properties.html](https://v8project.blogspot.se/2017/08/fast-properties.html)

[1] [https://github.com/pieroxy/lz-
string/pull/98](https://github.com/pieroxy/lz-string/pull/98)

[2] [https://en.wikipedia.org/wiki/Trie](https://en.wikipedia.org/wiki/Trie)

[3] [https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Refe...](https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Reference/Global_Objects/String/charCodeAt)

[4] [https://run.perf.zone/view/LZ-
String-2018-01-18-v2-151619850...](https://run.perf.zone/view/LZ-
String-2018-01-18-v2-1516198507426)

[5] [https://run.perf.zone/view/LZ-
String-2018-01-18-v1-151619752...](https://run.perf.zone/view/LZ-
String-2018-01-18-v1-1516197522874)

[6] [https://github.com/pieroxy/lz-
string/pull/98/commits/1894f7c...](https://github.com/pieroxy/lz-
string/pull/98/commits/1894f7c3765f5544299727885cbb4d9cbadd5db5)

~~~
kickscondor
I would expect that this is where WebAssembly could offer great help. You
could write your algorithm against flat memory rather than the inpredictable
data structures that JavaScript offers. Or port one from C.
([https://github.com/kripken/lzma.js](https://github.com/kripken/lzma.js))

~~~
vanderZwan
It would, but for the sake of backwards compatibility, these kinds of
optimisations don't hurt

Similarly, one reason I'm using canvas instead of WebGL is because it's very
easy to have hundreds of canvas elements on a page (for example when plotting
hundreds of little barplots).

WebGL, afaik, does not expect you to open more than _one_ WebGL view, and
requires some trickery to work around this. For example, this regl-in-idyll
example opens one "transparent" WebGL overlay over the whole screen and
"scrolls" along with the actual webpage:

[http://idyll-lang.org/idyll-regl-component/](http://idyll-lang.org/idyll-
regl-component/)

Which, admittedly, is a really cool hack! However, if you then take into
account that my employees like to _print_ their webpages, it turns into a pain
again...

------
blinkingled
>This also resulted in an 18% improvement in one of the benchmarks in the
Emberperf benchmark suite that tests Ember.js.

Somebody alert Atwood!

