Stream: general

Topic: Reusing memory


view this post on Zulip JMS (Jun 26 2022 at 23:32):

I have quite a complex use-case. I'm using wasmtime for a video game that's like screeps. Each player uploads a wasm script (written in Rust), and I create an Instance for each of their units each tick.

I want to somehow provide a way to persist memory between instances. I have a basic implementation working like this:

  1. Create a 1mb chunk of memory on the host
  2. Create an Instance
  3. Call a function exported from the instance to allocate 1mb of memory
  4. Copy the 1mb chunk into the instance-provided chunk (1)
  5. Deserialize the 1mb chunk with bincode into a T within the VM (2).
  6. Use that data
  7. Serialize the data back into the 1mb chunk using bincode (3)
  8. Host then copies the 1mb chunk in the instance back to the 1mb chunk stored on the host (4)

This involves 4 different copies and/or serializations. That's pretty sucky, and adds a lot of overhead. I've considered various schemes to avoid 2-3 by using mem::transmute() and building an allocator that allocates entirely within the byte slice. That would avoid needing to use bincode to convert to and from bytes within the instance. However, I still would need to copy the data in and out of the instance each time.

I think a better way would be to limit the memory to 1mb for the instance, and reuse it between instances. The questions are then:

  1. How do I reuse the memory?
  2. How can I provide a new instance the same memory, and have it pick up from where it left off on the rust side? Like, if I declare a static mut T or whatever, how can I have the script get that in another instance later on?

Ideally, if the wasm script panics, then all changes to the persistent memory would be rolled back. So I kinda want copies 1+4, but I want to avoid the overhead of bincode is the bigger issue.

view this post on Zulip Alex Crichton (Jun 27 2022 at 16:16):

Currently linear memories cannot be reused across stores, and you probably want to retain one-instance-per-store since otherwise instances never get deallocated (they're owned by the store). Linear memories can technically be shared between instances within the same store but it would mean you have to persist the store for a long time which may not be what you want.

Otherwise what you can do is if the modules import the memory you can manage the memory yourself. For example you could create the linear memory on the host and then when the wasm module is done with it you do what's necessary to serialize that to disk and such. Using the MemoryCreator trait you may be able to create custom schemes for memory allocation perhaps as well?

view this post on Zulip JMS (Jun 28 2022 at 03:20):

How exactly do I provide memory to the guest? Right now I'm calling a function to allocate memory from within the guest, and give the pointer back to the host to write to. Is there a way to just provide memory directly to the guest?

view this post on Zulip bjorn3 (Jun 28 2022 at 12:39):

Is there a way to just provide memory directly to the guest?

You can use the grow method on the Memory and then write to the newly grown pages. It will depend on whichever memory allocator the wasm module uses if it will handle this fine, or corrupts it internal state. In any case you probably won't be able to deallocate the memory again without corrupting the allocator state. You also can't map existing pages with data into the wasm linear memory. You must copy it one way or another.


Last updated: Dec 23 2024 at 12:05 UTC