-
Notifications
You must be signed in to change notification settings - Fork 695
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Shrinking memory and swap/switch memory or clone the instance it self to 'force' gc collecting the exponentially grown memory #1427
Comments
We did discuss shrinking memory in the initial design of Wasm memories. This is a really tricky area in the internal implementation inside of engines. In particular, shrinking a memory is hard to do without introducing many potential security vulnerabilities. Basically, revoking access to some memory at the end of an array buffer is problematic, as there can be inconsistent views of the size of memory. E.g. just consider some code deep in Chrome or FireFox that loops over the memory from beginning to end. (Legacy) code that caches the length between array iterations would then break if the memory is shrunk while it is iterating (either synchronously, in a deeply-nested call, or asynchronously, in another web worker, with a shared memory). Note that said code is subtly wrong if a memory grows, but it is not a vulnerability in the sense such code would just terminate early. This is the reasoning why growing is much safer. For this reason we settled on the current design where memories cannot be shrunk. We reasoned that applications that did want to shrink memory should be prepared to do the more complicated thing that you outline here. We've discussed other alternatives to actually shrinking the memory, e.g. |
Do these discussions exist transcribed anywhere, for those of us who'd like to come up to speed? - Casey |
It seems unfortunate that this has to be done outside of the WASM, rather than having some internal say Such a design would be rather nice for host integration in that things like JS could pass in a memory as an argument to WASM functions, let wasm do some heavy processing or whatever on the memory and just let go of the reference when it's done. e.g. As a simple bikeshed, something like: (module
;; Declare a memory that is swappable
;; Such memories can't have fixed memory locations given well they
;; can be swapped, by requiring such a declaration non-swappable memories
;; should should not suffer any penalty
(memory $data swappable)
(func $processData (export "processData") (param $inMem memoryref)
;; Swap the data in for processing
(memory.swap $data (local.get $inMem))
;; ...process the data here
;; Once done with the data swap the data for some empty data again
(memory.swap $data (memory.new 0))
;; Once this function is over, the memoryref for the param is no longer held
;; in WASM, once released in the host it can be freed safely
)
) Ultimately this would be safer for hosts because they can just release the memory when they release the reference. |
I experimented with wasm2js to see the difference in their design of using and growing the WebAssembly.memory. It turns out that they implement the reallocation and growing logic loosely making it even possible to instead of only growing but also shrinking it, because the implementation is simply: allocate a new and bigger ArrayBuffer and then set the content with the old heap. And the program works just fine when it already have memory freed (zero'd) then shrinked (with the exact steps except the ArrayBuffer is ofcourse, smaller). This is something that doesn't exist yet on current WebAssembly design, and I don't know if there is a difference in WebAssembly's internal implementation that prevents the memory works that way. Because if there's no difference, making the buffer property of WebAssembly.memory to be configurable would solve this issue completely.
Furthermore, I try to find a way to mitigate the limitation of non-shrinkable memory of wasm. The obvious solution is, you guessed it, running wasm on a web worker (or worker-thread on nodejs) so the whole memory and the worker itself can be copied then destroyed easily. While this should be a non-issue on nodejs as every nodejs' feature works in worker-thread, pretty much every DOM api even something comical like DOMParser (which should work fine in worker as it doesn't have any connection to DOM manipulation) doesn't work on web worker. And yes there is some project like worker-dom or comlink that tries to create a bridge to DOM api or any main-thread only api but the majority of it is still WIP or work differently (comlink is async only). Another solution is to keep reference or simply copy the buffer of currently running wasm instance, then unreference the instance (null will work fine) and reinstantiate a new wasm instance but don't run the main function just now, create a TypedArray view of the current memory buffer (instance.exports.memory.buffer), then set the content with the copied buffer in the early steps. With this steps the program will run just fine but... it is so anti-intuitive it feels like using comlink is now more approachable than doing this, not to mention that this process should run everytime we want to shrink the memory.
Even, in retrospect, if WebAssembly supports swapping or switching its current memory with a new one, or, supports cloning an instance so it shouldn't be reinstantiated (preferably synchronous, but async works fine) this would also solve this issue.
The text was updated successfully, but these errors were encountered: