Chrome, wasm32, 4GB (2GB?) limits, workarounds

  1. I'm using Rust compiled to wasm32 on Chrome.

  2. I understand 32-bit arch has a 4GB memory limit.

  3. I understand (and am using) web worker (separate thread) and js_sys::ArrayBuffer (does not count towards 4GB limit)

Question:

4a. I recall quite a few people have mentioned that the actual limit is 2GB, not 4GB, but I don't understand why. Can someone explain this ?

4b: Besides webworker, js_sys::ArrayBuffer, js_sys::SharedArrayBuffer, are there any other techniques for working around the 4GB / 2GB limit ?

From: https://doc.rust-lang.org/1.62.0/std/primitive.pointer.html#method.offset:

Basically the sign bit (from signed pointer arithmetic) makes it so you often have to halve the address space; thence the 2GiB :pensive:

EDIT: I thought the question was about a 2GiB limit on allocations, not on the total available RAM

Those are limits on a single allocation, not on the sum of all allocations.

2 Likes

@Yandros , @sfackler : Yes, I was going to make the same comment. I read the doc a few time, and the only thing I got out of it was: due to isize offset, a single Vec/Box can't be more than 2^31, but I don't see how to prove the entire heap is only 2^31.

IIRC, there was some reason the heap was "split" in wasm? Either code vs data (which would be kind of silly since code generally isn't 2GB) or some other reason.

ping @bjorn3 : Insights? I recall you being an expert in these topics.

If your pointers are 32-bit signed integers then I would assume the highest address you can reach is 2^31.

As far as I am aware, the multi-memory proposal hasn't been merged yet, so you can't increase your available memory by saying "pointer A should use memory block 1 and pointer B should use memory block 2".

In particular, the spec says

In the current version of WebAssembly, at most one memory may be defined or imported in a single module, and all constructs implicitly reference this memory 0. This restriction may be lifted in future versions.

(although I notice the wasmtime VM has an experimental flag that enables it)

There is also the requirement that linear memory is one backed by contiguous block of memory.

I'd try to modify the program so it doesn't require 2+ GB of memory at any one time (e.g. by processing inputs in smaller chunks). I'm assuming you've already tried this though or that there might be domain-specific reasons why you need more memory, so this suggestion is probably not helpful.

Where do you get the assumption that pointers are 32-bit SIGNED integers ?

From the above docs, all I see is that: offsets (within a single object) are 32-bit SIGNED integers, which caps Vec/Box to 2^31 bytes.

Right now, I'm just shoving data in ArrayBuffer , and loading them into wasm as needed. What I meant to ask in the question was if there were any other techniques, besides js_sys::ArrayBuffer / web_worker that might be useful in this situation. (I.e. other JS/wasm APIs I should learn about).

You can use the wasm64-unknown-unknown target instead. AFAIK it is still experimental in rustc though. I believe browser support is also still experimental if existent at all.

I believe chrome used to be limited to 2GB for internal reasons, but now allows the full 4GB.

Code is never part of the wasm linear memory. There is no limit on the size of code that I know of. 10GB of code should work fine I think, though actual implementations likely limit it to a fraction of that. For example chrome limits all (javascript) code to 128MiB I believe. I did expect the wasm code to count towards the same limit.

1 Like

For # of threads / figuring out # of web workers to start, there is Navigator.hardwareConcurrency - Web APIs | MDN

Is there something similar to query at runtime whether it is 2 GB vs 4 GB ?