Search code examples
rustwebassemblywasm-bindgenrayon

Only 1/4th of max memory available when rust wasm compiled with +atomics flag webassembly


So, I've been running out of memory with wasm/rust with +atomic flag and wanted to check how much memory is practically available. Here is my crude minimal working example that logs the memory of a vector before it panics:

index.js

import init from './pkg/test1.js';
import * as wasm_test1 from './pkg/test1.js';
async function run() {
  await init();
  let newDiv = document.createElement("div");
  let btn = document.createElement("button");
  btn.innerHTML = "Max out the memory now and panic!";
  document.body.appendChild(btn);

  btn.onclick = function () {
    wasm_test1.fill_memory();
  };
}
run();

lib.rs

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern "C" {
    #[wasm_bindgen(js_namespace = console)]
    fn log(s: &str);
}

#[wasm_bindgen]
pub fn fill_memory() {
    let mut v  = Vec::new();
    for i in 1..1000000000 {
        v.push(0);
        if (i % 10000) == 0 {
            let v_size = (std::mem::size_of_val(&*v)/1024/1024).to_string();
            log(&format!("{}", v_size+"Mb"));
        }
    }
    std::mem::forget(v);
}

Cargo.toml

..
[lib]
crate-type = ["cdylib"]

[dependencies]
wasm-bindgen = "0.2.80"
wasm-bindgen-futures = "0.4.30"

.cargo/config

[target.wasm32-unknown-unknown]
rustflags = ["-C", "target-feature=+atomics,+bulk-memory,+mutable-globals"]

[unstable]
build-std = ["panic_abort", "std"]

Here is what I observe:

No flags +atomics
max memory available 1024Mb 256Mb
Browser peak memory consumption (in task manager) 2550Mb 750Mb
Browser idle memory consumption (in task manager) 225Mb 225Mb

Note that +atomics also requires a couple of other flags to be present, but if I remove this flag (and keep the others) the behavior is identical to no flags. The available memory is very precise in the two cases before panic, 256Mb and 1024Mb. So what happens when I set memory flag for higher memory? The memory available with +atomics flag is consistenly, exactly, 1/4th of the total max memory.

I have a few questions:

  • Why is the memory 1/4th when using +atomic flag, and what can I do to improve this?
  • Why is total memory capped at 1 Gb?
  • Why does the browser use 2.5Gb memory when vector is only 1Gb (and maybe with a little overhead)?

Solution

  • For anyone running into this issue here, there were a couple of issues at play here. I was, eventually, able to claim all the memory.

    1. It appears that dynamically allocated vectors in rust work in a way that when elements are pushed and you run out of current space, the allocation is doubled. As such, it panicked closer to half the memory though more was available. ::with_capacity helped me out here
    2. There's some sort of default set in rust compiler with the +atomics flag that caps memory to 1Gb. You can change this default in the cargo config file by adding a rust flag, something like the below to get 4G (the max that chrome allows for WASM):
    rustflags = ["-C", "target-feature=+atomics,+bulk-memory,+mutable-globals", "-C",
     "link-arg=--max-memory=4294967296"]