Skip to content
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

Pooling allocator: switch to a struct-of-free-lists representation #6627

Closed
fitzgen opened this issue Jun 22, 2023 · 3 comments
Closed

Pooling allocator: switch to a struct-of-free-lists representation #6627

fitzgen opened this issue Jun 22, 2023 · 3 comments
Assignees
Labels
performance wasmtime:api Related to the API of the `wasmtime` crate itself wasmtime Issues about wasmtime that don't fall into another label

Comments

@fitzgen
Copy link
Member

fitzgen commented Jun 22, 2023

Rather than our current free-list-of-structs representation.

That is, right now we have a single free list where each slot has space for the vmctx, table(s), memory(s), etc. This is wasteful in a component-model world, where each instance in the component is going to reserve a full pooling allocator slot of tables/memories/etc even if it is not using all of that state (e.g. the instance only imports memories).

If, instead, we had separate free lists for each of vmctxs, tables, memories, etc... then we can allocate the precise number of things that the component will use, without any slop.

This does mean that we will do len(vmctxs) + len(tables) + len(memories) + ... free list allocations rather than only len(instances). This is probably fine, however, because we can have a single mutex protecting all free lists rather than per-free-list locks, and the actual free list allocation is (probably?) pretty cheap in comparison to the locking involved. Note also that only the free list for memories cares about affinity, so we can use a simplified and optimized free list implementation for all other entities.

@fitzgen fitzgen added wasmtime:api Related to the API of the `wasmtime` crate itself wasmtime Issues about wasmtime that don't fall into another label performance labels Jun 22, 2023
@alexcrichton
Copy link
Member

I think that concretely the resources to manage at this time are tables, memories, and stacks. The instances (vmctxs) were moved into malloc awhile back and are no longer managed by the pooling allocator. Stacks are actually already in the struct-of-free-list form where stacks have a separate index allocator from tables/memories. Tables and memories, however, are bundled together as one allocator.

In terms of committed memory I don't think a struct-of-free-list approach will help much because deallocated slots have their memory decommitted. In terms of VM space I think it can improve, because if an embedder wants to allow a component to take up to M linear memories then supporting N concurrent instances may not require M*N slots of linear memory (assuming not all components take M linear memories).

In terms of performance I don't think this will be much of an issue though. The mutex around the free list has not been a bottleneck historically so splitting that into two mutexes I don't think will be much of an issue. This'll allow the affinity strategy to be used just for linear memories and tables could use a simpler allocation scheme too.

When we talked about this we were thinking that all we would need to add is an descriptive error of why instance allocation failed if it did, but thinking on it more I think we'll need to add accessors for how many resources each component/instance acquires. For example let's say there's an embedding that wants to support 1000 concurrent instances where some components could take up to 4 linear memories. The embedding doesn't want to reserve VM space for 4000 linear memories, so instead it reserves 1500 linear memory slots in the VM address space. In this situation the embedder may want to handle components that actually require 4 linear memories a little differently than other components. For example linear memories could be broken down as: 500 for 1-memory instances, 200 for 2-memory instances, 100 for 3-memory instances, and 75 for 4-memory instances. If a 4-memory component is being instantiated but all 75 slots are already taken the embedder may wish to queue up the instantiation independently of whether there's actually space for it.

Basically that's just a long-winded way of saying I think this is a good idea to implement, but I think we'll want to be able to, from a compiled &Module or &Component, learn how many resources are going to be consumed. That plus a descriptive error when allocation fails I think should be good enough for now at least for embedders to integrate components better.

@fitzgen fitzgen self-assigned this Jul 28, 2023
@fitzgen
Copy link
Member Author

fitzgen commented Jul 28, 2023

Gonna start poking at this.

fitzgen added a commit to fitzgen/wasmtime that referenced this issue Jul 31, 2023
Returns a summary of the resources required to instantiate this module or
component.

Potential uses of the returned information:

* Determining whether your pooling allocator configuration supports
  instantiating this module.

* Deciding how many of which `Module` you want to instantiate within a fixed
  amount of resources, e.g. determining whether to create 5 instances of module X
  or 10 instances of module Y.

Part of bytecodealliance#6627.
fitzgen added a commit to fitzgen/wasmtime that referenced this issue Aug 1, 2023
Returns a summary of the resources required to instantiate this module or
component.

Potential uses of the returned information:

* Determining whether your pooling allocator configuration supports
  instantiating this module.

* Deciding how many of which `Module` you want to instantiate within a fixed
  amount of resources, e.g. determining whether to create 5 instances of module X
  or 10 instances of module Y.

Part of bytecodealliance#6627.
github-merge-queue bot pushed a commit that referenced this issue Aug 1, 2023
Returns a summary of the resources required to instantiate this module or
component.

Potential uses of the returned information:

* Determining whether your pooling allocator configuration supports
  instantiating this module.

* Deciding how many of which `Module` you want to instantiate within a fixed
  amount of resources, e.g. determining whether to create 5 instances of module X
  or 10 instances of module Y.

Part of #6627.
eduardomourar pushed a commit to eduardomourar/wasmtime that referenced this issue Aug 18, 2023
…ealliance#6789)

Returns a summary of the resources required to instantiate this module or
component.

Potential uses of the returned information:

* Determining whether your pooling allocator configuration supports
  instantiating this module.

* Deciding how many of which `Module` you want to instantiate within a fixed
  amount of resources, e.g. determining whether to create 5 instances of module X
  or 10 instances of module Y.

Part of bytecodealliance#6627.
@fitzgen
Copy link
Member Author

fitzgen commented Aug 25, 2023

This was fixed in #6835

@fitzgen fitzgen closed this as completed Aug 25, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
performance wasmtime:api Related to the API of the `wasmtime` crate itself wasmtime Issues about wasmtime that don't fall into another label
Projects
None yet
Development

No branches or pull requests

2 participants