Efficient web worker data transfer strategies

I’ve recently converted my boids code to run on a web worker. Since it is a non-interactive simulation it seemed fit for the optimization, but apparently I had forgotten that web workers never work well :smiley: Anyway I got stuck with two issues and the strategies I started to devise became too complicated, so I’m hoping someone here already went through the hoops and has some tips for me (and for you, reader of the future).

So it’s a very simple producer/consumer architecture, where the web worker updates the 3D position and velocity of an array of objects on an infinite loop, calling requestAnimationFrame() repeatedly. It sends two arrays of BABYLON.Vector3D through postMessage(). The issues I’m having:

  1. I thought/misremembered that deserialization would happen in the background browser process, but accessing event.data in onmessage() is stealing time from the main thread.
  2. Sometimes the simulation loop is faster than the main thread, which ends up sending more than one update for each frame of the main thread. This is useless and wastes even more time of the main thread.

Problem 2 was partially solved by storing the event that was sent in a local variable. As long as I don’t access event.data the deserialization is not triggered, so this ensures only the last event is processed (as long as I don’t async too much). This works as long as you can miss messages, and apparently there’s not much time wasted in the web worker for serializing data.

To handle problem 1 I considered a double-buffering approach, but it seems hard to handle signaling to swap the buffers between the threads. A N-buffer approach where you send buffers back as you process them and ensure that N is big enough to avoid an empty queue in the producer should work. A simpler solution is to allocate a buffer on the worker thread every time and send it as a transferable object, but I’m wondering if I’d not be triggering the GC so hard that it’d be worthless (I’m simulating about 100 to 1k objects on each worker, by the way).

As a side question that relates more directly to BabylonJS, was an array of vectors structure ever considered? Vector3 backed by ArrayBuffer? seems to be about using an ArrayBuffer for a single Vector3, but I couldn’t find anything about an ArrayOfVector3 object (as well as ArrayOfXxxxx which would use a single ArrayBuffer internally for all the vectors. I assume that the Buffer class handles most of the internal needs of BJS, and ArrayVector3 could probably wrap around it. I have no idea if it’d be worth to pass it to/from wasm but for some operations on/between arrays it might handle code vectorization with SIMD very well.

1 Like

I am doing compute work with web workers, and I decouple the data from complex objects (such as babylon.vector3) so it can be sent as arraybuffer (intarray32 in my case) which is an transferrable object this has a zero-copy and should be quite effective.

When doing messaging with web workers I throw an error if the object received is not an arraybuffer so I am sure the data is zero-copy and has minimal overhead in the transfer process.