Skip to content

Proposal: Replace data structures with tagged arrays #4020

Description

@gorbak25

Summary

Might be a necrobump of #2577
Currently pattern matching on union types discriminates the types in the union using instanceOf. This incurs a significant performance penalty for larger union types and makes it infeasible to dispatch the code based on the type(switch on a tag instead of an if(... instanceof ...) ladder. Additionally construction of large data structures is slow due to the need to constantly allocate new heap objects(instead of being JS literal). JIT is affected as accessing a property/walking the prototype chain is not cheap.
Consider replacing data structures with an array ["TAG", value0, value1, ..., valueN]

Motivation

After running sed -i 's/instanceof/.constructor ===/g' dist/bundle.js on https://github.com/erlscripten/erlps-aesophia I've decreased the runtime by 6%. This makes sense as erlscripten is based on the following union type - https://github.com/erlscripten/erlps-core/blob/c881e39901a2d6c29972b8a13c3c1ee760754560/src/Erlang/Type.purs#L42
I've started benchmarking various alternatives to instanceof and came to the conclusion that by just keeping everything in an array we generate fast JS which also can be easily optimized by the JIT compiler.
For

data Tree = Node Tree Tree | Leaf Int

foo :: Tree -> Int -> Int
foo (Node l r) acc = foo r (foo l acc)
foo (Leaf x) acc = acc + x

main :: Int
main = foo (Node (Node (Node (Leaf 1) (Leaf 2)) (Node (Leaf 3) (Leaf 4))) (Node (Node (Leaf 5) (Leaf 6)) (Node (Leaf 7) (Leaf 8)))) 0

Keeping everything in an array yields an 95%+ performance improvement https://jsbench.me/beklw3j28m/2
I've also noticed that construction of large terms in erlscripten is suspiciously slow(due to the usage of "new")

Proposal

Replace new Foo(a1, a2, a3) with ['Foo', a1, a2, a3]
Replace v instanceof Foo with v[0] === 'Foo'
Replace v.valueN with v[N+1]

Examples

Take a look at the benchmark: https://jsbench.me/beklw3j28m/2

Small question

In the Erlscripten project we plan to maintain and ship a custom superoptimizing Purescript compiler.
Right now it lives here: https://github.com/erlscripten/purescript/commits/erlscripten-lambdaelim It includes some unmerged fixes in this repository and it features a completelly reworked codegen which never generates additional lamdas(it uses let statements instead of lambdas to save on stack space, this also allows for TCO to be applied everywhere). We will implement this proposal on our fork next week. Would you be interested in a PR? Is this a feature which can be upstreamed?

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions