Can I cache the memory.buffer from wasm?

I'm constructing an array in rust with 30,000 elements. By following the wasm Game of Life example, I learned how to access my array from JavaScript with the following every tick (1/60 of a second):
const bcell = new Float64Array(barrage.__wasm.memory.buffer, this.bulletsPtr, this.bulletsLen);

The only problem is it's too slow for 60 fps. I did some profiling and the act of creating that Float64Array takes 15ms in my case. If I comment the above line, I go back up to 60 fps even though I'm still reading from the entire array.


To speed things up, I thought I would be able to cache the creation of this array in the constructor of my object, but it doesn't work. It seems to only work if I create the array anew for each tick.

I can even do something hacky like initialize the array only once or even 10 times in the tick function, but then all the bullets freeze in time after I stop creating a new array object because it holds the same values.

My understanding was the Float64Array should just be a view over a block of memory, so I thought I should only have to create it once, but that doesn't seem to be the case.

Is there a way to just cache the creation of this Float64Array? I think I'm dealing with the same view over the same block of memory every tick. The size of my array doesn't change.

If you look at the Float64Array constructor docs, it's specified to copy the data into a new typed array.

Edit: I think I was mistaken here. If you're using the 3-argument constructor variation, and passing in an arraybuffer, I have no idea why it is holding old values alive.

Maybe you could try using a DataView instead, just to test? It won't be as nice to access as a Float64Array, but it does have a getFloat64 method...

Tried the DataView, but I still have to recreate the array every time. Thanks anyway.

How are bulletsPtr and bulletsLen calculated? One thing that might be causing this to fail is if you're using different memory each tick - like creating a new vec each time, or adding enough elements to a vec to make it reallocate. Are you sure the data is in static memory and it's position/size is unchanging?

So bulletsPtr is calculated like this:

cells is an array. I'm doing the same thing except with my bullets array.

bulletsLen is just the number of bullets I have multiplied by how many floats are in my bullet struct, which is 5 (two Vector2s with an x and y, and a float for the angle).

I only create the vec once in Rust when the spawner is initialized and no new elements get added over the course of the vec's lifetime.

Every tick, the bulletsPtr, bulletsLen values are the same. The memory buffer is only the same (all zeroes) if I initialize in the constructor. In tick, the values are different, which is why I'm confused.

Maybe I'll work on a minimal reproducible example and see if the problem still exists.

1 Like

I finally created an MPR to test this, and I can indeed create the Float64Array only once in the constructor and read from it every tick. It does seem to be what I thought initially, which is just a view over some memory. However, I can also create a new Float64Array every tick and it seems to have no impact on performance.

Either I'm doing something weird in my bigger project, or Electron is doing something behind the scenes that is affecting wasm performance. I may also just be importing my wasm file the wrong way. I'm currently doing this:

let barrage = require('electron').remote.require('barrage');

as that was the only way I could get it to work. I may try importing it like my MPR in index.html and see if it makes any difference:

    <script async type="module">
      import init, {Spawner} from './pkg/mpr_bullets.js';

      async function run() {
        let wasm = await init();
        window.Spawner = Spawner;
        window.memory = wasm.memory;
      }
      run();
    </script>

It turns out this was due to how I was importing the wasm file in Electron. I was building the wasm and importing like this:

wasm-pack build --target nodejs
let barrage = require('electron').remote.require('barrage');

When I switched to importing in index.html, the performance issues went away:

wasm-pack build --target web

<script async type="module">
  import init, {Spawner} from './pkg/barrage.js';

  async function run() {
    let wasm = await init();
    window.Spawner = Spawner;
    window.memory = wasm.memory;
  }
  run();
</script>
1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.