"Free" performance improvement: preallocate arrays (when possible) throughout Babylon.js's codebase

After reading through a portion of Babylon.js’ codebase, I’ve noticed there are many cases where an array is created and then pushed onto, when it could be pre-allocated ahead of time. Preallocating arrays reduces GC pressure and often improves performance considerably, especially when the array is large.

Examples:

I can submit a PR to do this in a few places if you want.

4 Likes

Please do! that would be awesome!

This looks great, is there an update on this?

I started thinking about this. I’m looking at _generatePointsArray

here is one approach to speed the think up a bit.

change:

        this._positions = [];

        for (var index = 0; index < data.length; index += 3) {
            this._positions.push(Vector3.FromArray(data, index));
        }

to:

        // reuse the array - if not existend create a new 
        this._positions = this._positions || [];

        // pre set the length, or cut only so much that it fits the new positions array
        // (GC optimisation) and speedup. Index access is fast then push
        this._positions.length = data.length / 3;

        for (var index = 0; index < data.length; index += 3) {
            // put with index on pre allocated array
            this._positions[index / 3] = Vector3.FromArray(data, index);
        }

However the full potential is not reached, because the _positions array is cleaned elsewhere. Im looking into it :slight_smile:

2 Likes

I ended up overwriting some of these functions

I have a DataViewBackedVector3 and a Float32BackedVector3 which partially implements the interface from Vector3.

For _generatePointsArray, rather than copying the entire positions array into a bunch of Vector3 objects, it reuses the same positions buffer from the Geometry and just maintains 3 DataViewBackedVector3 objects and updates the offsets the DataViewBackedVector3 objects are looking at. This avoids using extra memory and reduces GC pressure.

The Float32BackedVector3 conditionally uses a SharedArrayBuffer (when available) which lets other threads do things using the player’s current position.

Do you see any major performance issue? (we did in the past)

What is the specific performance issue you’re thinking of? The evaluate active meshes call is slow but that was already slow

One thing I’m careful of doing is not accidentally copying the contents of the Float32BackedVector3 very much — directly accessing position[0] or position.x instead of const x = position.x; I think that might help somewhat

I was doing some tests like transforming 10000 vertices by a matrix and this kind of stuff

Oh hm I wouldn’t notice many issues like that because this is mostly used for terrain with frozen world matrices. I do notice that TransformNode.computeWorldMatrix shows up in the profiler and there aren’t many of them (since that’s the player’s model) but I think it showed up with Vector3 too.

If you send me some code to copy-paste I’m happy to run it

No worries, it was more out of curiosity :slight_smile:

If is saw it correctly the _generatePointsArray is improved. :heart:

I can confirm that it is a huge performance boost (checked out latest preview )! For picking after geometry manipulation

2 Likes