-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Add a wasmtime::Memory::discard
function
#9572
Comments
The Wasm language semantics dictate that a linear memory cannot shrink. If Wasmtime added a memory-shrinking API, that would violate the Wasm language semantics. As a project, we care deeply about standards compliance and therefore we consider all deviations from the standard language semantics to be bugs and would never intentionally introduce APIs that enable such deviations. The more that Wasm engines deviate from standards the less portable Wasm becomes in practice, toolchains need to know which engine they are targeting and what that engine's nonstandard behavior is, etc... So, unfortunately, this proposal as written is not something we would entertain. That said, there are some workarounds:
I'd recommend investigating (1) and (2). Let me know if anything I described wasn't clear or if you have any other questions. |
This brings to mind an interesting standards-compliant option: we could, in theory, implement memory zeroing operations ( |
|
Sure -- I guess my general point was that we could in theory implement this optimization today (by recognizing an aligned (And the beauty of having well-defined semantics means that on Windows we could still do a |
I understand the logic, however this is a case where seemingly sound logic leads to arguing in favour of the worst option. This is the worst option because it involves dancing around a basic problem in absurd and inefficient ways (recreating the memory but smaller, reinstantiating the module, actually reinitialising the module from the start, not actually shrinking memory but somehow telling different OSes that it's not really needed anymore) when simply we should be able to shrink memories and it shouldn't involve copy operations or anything like that, we should just be able to resize memories externally. If I somehow implemented the suggested function in my library outside of Wasmtime (I don't think I'd know how to do this sadly, I don't suppose I can just
I mostly disagree with this because what I'm proposing isn't something that affects Wasm modules and how they're made in the sense that it's about the host unilaterally shrinking memory at its own risk. From the point of view of the Wasm module imagine this: your allocator grows the memory with If you think about it this way as opposed to thinking about an active
The whole linear memory will be touched because it only gets enlarged as a result of allocations on the heap, and we can presume that allocations aren't done for nothing and every page will be touched at some point. Another detail is that my allocator writes
While that's an interesting idea it's only applicable in a subset of situations and it changes how everything works, just to avoid shrinking memory. It wouldn't work so well if using lots of memory is part of the initialisation of the module and not what it does after initialisation, unless you reinstantiate after the actual internal initialisation, but once again it all comes down to a roundabout way of shrinking memory. Generally just imagine you have something like MS Paint as a Wasm module, your image is the last buffer in the heap and you just resized your image from 20,000 x 15,000 pixels to 1000 x 750, you wouldn't want to reinstantiate anything, you'd want the same things in memory but you wouldn't need a bunch of what you used before.
My library is meant to be maximally portable because to me the whole point of Wasm is that it can run almost anywhere, even where I didn't think it would run, so whatever I do has to work anywhere Wasmtime can work. |
@Photosounder I might encourage you to approach this problem with some more curiosity to different thoughts and opinions about how to solve it. Describing alternatives as "absurd", calling existing modules as "careless", and saying that all existing modules should "simply" do one thing at least gives the impression to me that you're not taking any positions seriously other than your own. Being a maintainer of a project involves balancing concerns all the time, for example neither bug reports nor maintainers are 100% correct all the time, so solutions often require balance and understanding from all parties involved. If you're only interested in solving this problem in one way and are unwilling to engage in design then this is unlikely to see a solution. |
I apologise for my colourful language, I've always been a bit blunt, mostly by non-French standards (in some countries people only ever say nice-sounding things, so culture shock makes things worse). When I say that something is absurd I don't mean it as an attack, I mean that doing something complicated and slow to avoid doing something simple and fast is absurd because that's what it is. |
Hi @Photosounder -- I can appreciate your frustration here, seeing inefficiency that doesn't seem necessary when considering your use-case, but echoing Alex above, I don't think it's coming off in a particularly productive way. I want to try to help illustrate "the other side" -- what are the constraints we are considering here? (Basically: please assume we're acting rationally, and wouldn't do something "absurd" without reasons forcing us to.) The main constraint we deal with in this context is standards-compliance. We are working in the context of an umbrella organization that believes in open standards and cross-compatibility. We believe strongly that if we deviate from the standards by enabling other functionality, we could lead the ecosystem down paths that are globally worse than if we held back. This is because of the practical dynamics of how the community, the tools and their users work. For example: say that we enabled an extension to shrink memory, and it became used and essential by common guest-plugin frameworks. Other Wasm engines might then feel pressure to add this feature too. It might interact in strange ways with other Wasm features that exist now or are under consideration -- multithreading makes this interesting in particular, as alluded to above, because of the possibility of a TOCTOU bug where one thread checks in bounds, another shrinks, then the first accesses (now out of bounds). The way that such interactions are usually discussed is in the context of the Wasm Community Group (CG), which is part of the standards body that specifies Wasm under W3C. You may think this is absurd or too slow or ... but it's important to give all features their fair discussion to find issues like this. There is a history of late-changing realizations in some features that might not have been caught if things had been baked in and stuck forever too early. We care about being a "good citizen" here and not forcing anyone's hand by shipping a thing that will become a de-facto standard. If we do that, we circumvent the whole process and reduce the number of options available. This could mean that we never get to ship other features because they would be incompatible / cannot be combined with the semantics we're now stuck with. Or it could mean that we have to deprecate the thing and ship a new version of the thing that is compatible, but support the old version "forever" because it's in wide use, which adds to our maintenance and testing costs. Either one is bad. For all these reasons, when we have a need where the existing Wasm standard doesn't allow for a use-case to be expressed efficiently, we go to the standards group and propose a fix. @fitzgen actually has a great recent example with his custom-page-sizes proposal: it allows systems with tiny memories to express those tiny memories in standard Wasm, without relying on proprietary extensions, and all its interactions with the rest of Wasm were thoroughly discussed. Now that it's a standard proposal, we can integrate it, and we have. I'd encourage you to see if you can use one of the workarounds we've discussed above; in parallel, please do feel free to engage in the Wasm CG's processes and voice your support for the "memory control" proposal or others as needed. Thanks! |
Thank you for explaining your point of view. I can see how you're right to reject my proposal. In a way there's a natural conflict. You're right to not create problems by going against the standard, I'm right to ask for what would benefit me, and the people in charge of the standard are right to be cautious about how they're changing their standard. And as you said for me it's annoying that something technically simple that would solve a problem would be denied in favour of something technically much more twisted. In a way my mistake is to focus on the immediate technical aspect. But also it really feels like something that should be directly solved in some way, even intuitively it feels wrong that something can only grow but never shrink. Which brings me to an idea I just had. Since you guys can't add a function that contradicts the standard, maybe you can add a function that doesn't contradict the standard, yet solves the problem on demand. I've been focusing on actually shrinking the memory because from a technical point of view this is simple and trivial to implement in Wasmtime, but since we can't actually do this for all the reasons outlined we could have a function much like what I suggested but instead of actually shrinking the memory it could mark memory pages as committed (please note that I don't know much about the topic of directly dealing with pages in that kind of way, so not only do I not fully appreciate the technical feasibility of this but also that's why I'd prefer if it was done by Wasmtime so I wouldn't have to write multi-platform code to do this myself) which in practical terms would (I suppose) adequately deal with large amounts of memory becoming unused. I have no idea if simply using "committed" pages would make the resident again, so I'd like to know what you think about this idea. Also in a way I've been barking up the wrong tree, I should be asking the people in charge of the standard to open their minds to the possibility that maybe not everything can grow forever, that shrinking memories at least externally is something that could/should be possible and that the language of the standard should reflect that possibility. |
I think adding something like a We wouldn't implement this for And probably we would want it to fail for memories that use a custom page size. Would this address your needs @Photosounder? (Aside: this is a nice example of how being curious and open can lead to new ideas, potentially better than any initial proposal, that no one was originally considering. Thanks everyone for contributing to this thread!) Footnotes
|
Yes this sounds like my latest suggestion, so this would suit me quite well in the sense that at least this would prevent large unused memories from being a practical problem. This just made me read about As for the name, if it follows the same principle as |
wasmtime::Memory::discard
function
I've updated the issue title to reflect the new intentions here. @Photosounder are you interested in trying your hand at implementing this feature? |
Thanks, but I don't know Rust at all and like I said the topic of dealing with OS pages is new to me so it's best left to someone competent 😉 |
Edited by @fitzgen: This issue has evolved. For the up-to-date description, see this comment
Original Issue Text
Feature
While
memory.discard
isn't here yet, we could use a way to shrink Wasm module memory without having to reinitialise it. I propose a Wasmtime function to simply shrink the linear memory.Context
I have a C library with which I can make native host programs that call Wasm reactor module functions sequentially using Wasmtime and the modules have access to one C function to send commands back to the host. Lots of things can be done this way, as it is I could even shrink memory by reinitialising it, but there could be a better way. In many cases I can even see the exact contents of a module's heap from the host so I can see how much free space there is at the end of the heap.
Benefit
In some situations a Wasm module can temporarily need lots of memory. As it is we're not really able to then release that memory without reinitialising the module. If we had a way to simply directly shrink the linear memory buffer to a given number of pages that would go a long way. In my case the host could shrink it either by user input, its own determination (many of my modules use an allocator that allows the host to clearly identify all the allocations on the module's heap) or the module could even send a message to the host requesting for the memory to be shrank either right away or after
wasmtime_func_call()
is done.Implementation
I think a simple function (in terms of the C API, I don't know about the Rust side) like
wasmtime_error_t *wasmtime_memory_shrink(wasmtime_context_t *store, const wasmtime_memory_t *memory, uint64_t new_size, uint64_t *prev_size)
would do nicely, and it would very simply just shrink the linear memory buffer. We havewasmtime_memory_grow()
so why notwasmtime_memory_shrink()
.Alternatives
Without this I would wait for the module's function to be done executing, copy its memory up to the new size somewhere else, reinitialise the module so it has a new memory, grow it to the desired size and copy the copy back into the module's new linear memory. My proposal would make things smoother. Even better if it's something effectively just like calling
realloc()
on a buffer so that shrinking regularly by a page or two would happen in no time.The text was updated successfully, but these errors were encountered: