How does Web Assembly deal with memory in general?

Hi!

We are considering shifting from asm.js to Web Assembly (wasm) (Unity 2018.2.14f1)

What we are concerned about is how wasm deals with memory (to avoid running out of memory errors). We have gone through most wasm unity blogs and posts, however a few things are still unclear:

1- In asm.js, Unity’s blogs stated that the content required a contiguous block of memory (the heap we request in Player settings). Does wasm also use contiguous blocks?

2- Unity blogs state that in wasm, the heap size keeps on increasing as it is needed. By what factor does it increase each time, 2x?

3- Unity blogs state that in wasm, the heap size keeps on increasing as it is needed. Is it possible to set a maximum limit till which we want the heap to grow automatically? Does the following emscripten argument do that:

PlayerSettings.WebGL.emscriptenArgs = "-s WASM_MEM_MAX=512MB";

4- Unity blogs state that in wasm, the heap size keeps on increasing as it is needed. Is it possible to set a starting heap size from which the heap size can keep on growing?

Using asm.js, we needed 435 mb total heap memory for our content to run. Without any min and max limits, wasm increases the total heap memory to 536 mb where 170 mb is free (and hence waste). Any suggestions for this?

Would be very grateful if you can answer as many of the above as possible.

Thanks.

1 Like

Hello ammar_12435.

Yes, WebAssembly memory is allocated as a contiguous block.

In latest Unity versions, and in Unity 2020.1 in particular, the algorithm is the following. If the current memory size is less or equal to 512MB, then the size of the heap doubles. If the current memory size is larger than 512MB, then it is gradually increased towards 2GB using the following formula 0.75 * currentMemorySize + 0.25 * 2GB. This means that it is getting closer and closer to 2GB but never reaches it, as heap size over 2GB is currently not supported (https://bugzilla.mozilla.org/show_bug.cgi?id=1392234)

Yes, “-s WASM_MEM_MAX=512MB” will set the maximum size of the heap to 512MB. Note that if you run over this limit, your build will crash. WASM_MEM_MAX is used in situations where heap should not be resized, for example, for multi-threaded builds.

Yes, you can achieve this using:

PlayerSettings.WebGL.emscriptenArgs = "-s TOTAL_MEMORY=32MB"

You can try the following:

PlayerSettings.WebGL.emscriptenArgs = "-s TOTAL_MEMORY=55MB"

After 3 resize iterations your heap will raise up to 55MB * 2 * 2 * 2 = 440MB.

2 Likes

Thank you for the detailed answer to all my questions! That is enormously helpful.

The emscripten arguments are working quite as expected but there is a small ambiguity.

If for instance I set:

PlayerSettings.WebGL.emscriptenArgs = "-s [ICODE]TOTAL_MEMORY=55MB"

The total memory wasm allocates in the start is 67MB. Why does it allocate an additional 12 mb?

I tested with different values and here are the results in the format (set value, actual value):
1- (55 MB,67 MB)
2- (43 MB, 50 MB)
3- (435 MB, 469 MB)

From what I have observed, TOTAL_MEMORY sets a little MB’s more than what we intend and that value is not constant.

1 Like

Hi,

I’m a little late to this party. I’ve been around all the links and Github threads and Unity docs.

As far as I gather, browsers are only now slowly trying to move towards allowing for allocations > 2GB. Great.

In unity however, My experience was:

  • Set emscripten args with SetPlayerProperty
  • No, that is obsolete, use the explicit API, e.g. PlayerSettings.WebGL.emscriptenArgs = “-s ALLOW_MEMORY_GROWTH=1”;
  • No, that has no effect, it seems. At least doesn’t allow me to go over 2GB. Also, emscriptenArgs is undocumented (Unity - Scripting API: WebGL), though there is PlayerSettings.WebGL.memorySize. Interesting
  • No, that setting is deprecated and has no effect at all (Unity - Scripting API: PlayerSettings.WebGL.memorySize)
  • Ultimately, I am circled back to the following: Unity - Manual: Memory in Unity WebGL , which basically says: “You want memory control. Well sorry, you can’t have that”.

All in all, a frustrating research experience. A lot of outdated information out there :frowning:

For now it seems, there is no way for me to get >2GB allocated in WebGL builds, and I’ll have to just live with that. Please, oh please, let me know if I’m mistaken.

SetPlayerProperty is indeed obsolete for setting Emscripten args. If there is an official manual documentation that refers to this, please let us know.

It is unfortunate that forum posts go out of date like this.

The explicit PlayerSettings.WebGL.emscriptenArgs API is the current method that is supported.

PlayerSettings.WebGL.emscriptenArgs = “-s ALLOW_MEMORY_GROWTH=1”; did not seem to have an effect, because ALLOW_MEMORY_GROWTH=1 is actually the default enabled option. If you wanted to disable memory growth, you could set “-s ALLOW_MEMORY_GROWTH=0”.

Thanks for pointing that out. I think at some point the intent of emscriptenArgs has been to be an internal workaround tool, and to some extent, it still is. Added a note to add docs for this, it should be mentioned there.

When ALLOW_MEMORY_GROWTH=1 is in effect, then memorySize specifies the initial heap size at startup, but it can freely grow at runtime. So practically it no longer has an effect.

Marked down a note to update the docs on memorySize to explain this.

That is unfortunately true. 2GB is the hard limit in Unity WebGL builds today, and also the hard limit in many web browsers today. Browsers have begun to work on 4GB support, but unfortunately it does not yet work in any browser.

You can try that by running

console.log(new Uint8Array(410241024*1024 - 65536).length);

in your web browser developer console. If it prints an error, it means that 4GB WebAssembly heaps are not yet supported. Testing out today on Chrome and Firefox on Windows, neither can make it work. To test 2GB heap support, run

console.log(new Uint8Array(210241024*1024 - 65536).length);

which should print out 2147418112 if supported.

2 Likes

See also Add 4GB heap support to roadmap? · Issue #214 · WebAssembly/website · GitHub, for a related conversation.

1 Like

HI i got this error :
stderr:ERROR:root:TOTAL_MEMORY must be larger than TOTAL_STACK, was 0 (TOTAL_STACK=5242880)

I Try to increase the memory like that :
PlayerSettings.WebGL.emscriptenArgs = "-s ALLOW_MEMORY_GROWTH=1" + "-s TOTAL_MEMORY=35MB" + " -s ERROR_ON_UNDEFINED_SYMBOLS=0 " + emscriptenLinkerFlags; but that’s not work, and i completly stuck :confused:

“-s ALLOW_MEMORY_GROWTH=1” + “-s TOTAL_MEMORY=35MB” + " -s ERROR_ON_UNDEFINED_SYMBOLS=0 "
is the same as:
"-s ALLOW_MEMORY_GROWTH=1-s TOTAL_MEMORY=35MB -s ERROR_ON_UNDEFINED_SYMBOLS=0 "
You missed some spaces in some parts…

And Unity’s default since 2019.x is to allow memory growth, so you should set it (to zero) only if you want to disable memory growth. I think that setting it to 1 will change nothing.

Thanks, for your reply, i change to that :

PlayerSettings.WebGL.emscriptenArgs = "-s TOTAL_MEMORY=35MB -s TOTAL_STACK=1MB " + emscriptenLinkerFlags;

I can now compile without any error, but when i go to my browser he infinite load … Before i make those script change, he load good, but give me an out of bound memory that’s why i make those script

In emscripten 1.9+ 4GB heaps are supported on V8 vms (Chrome, Edge, etc)

Where is the 2GB limit and how can i change it to 4GB?

SO… I found the constants in client.framework.js and changed them, but apparently the 2GB limti is built into the ELGS interface.

So, as of today, it can’t be done.

This is a bug and shoudl be fixed. 2GB is really an untenable memory limit for a 3D program, especially one where it needs to load assets through that memory before they can get to the graphics card.

Uncaught TypeError: WebGL2RenderingContext.bufferSubData: Argument 3 can't be an ArrayBuffer or an ArrayBufferView larger than 2 GB
    _glBufferSubData https://localhost:5001/client/Build/client.framework.js:12459