Skip to content

Commit 36a4fcc

Browse files
authored
Merge pull request #3191 from alexcrichton/refactor-spin-config
Refactor configuration env vars for pooling
2 parents abedc05 + cdfde50 commit 36a4fcc

File tree

1 file changed

+60
-8
lines changed

1 file changed

+60
-8
lines changed

crates/core/src/lib.rs

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ impl Default for Config {
8282
inner.native_unwind_info(false);
8383

8484
if use_pooling_allocator_by_default() {
85+
// Baseline for the maximum number of instances in spin through
86+
// which a number of other defaults are derived below.
87+
let max_instances = env("SPIN_MAX_INSTANCE_COUNT", 1_000);
88+
8589
// By default enable the pooling instance allocator in Wasmtime. This
8690
// drastically reduces syscall/kernel overhead for wasm execution,
8791
// especially in async contexts where async stacks must be allocated.
@@ -92,23 +96,71 @@ impl Default for Config {
9296
// supported though as an escape valve for if this is a problem.
9397
let mut pooling_config = PoolingAllocationConfig::default();
9498
pooling_config
95-
.total_component_instances(env("SPIN_WASMTIME_INSTANCE_COUNT", 1_000))
99+
// Configuration parameters which affect the total size of the
100+
// allocation pool as well as the maximum number of concurrently
101+
// live instances at once. These can be configured individually
102+
// but otherwise default to a factor-of-`max_instances` above.
103+
//
104+
// * Component instances are the maximum live number of
105+
// component instances or instantiations. In other words this
106+
// is the maximal concurrency that Spin can serve in terms of
107+
// HTTP rqeuests.
108+
//
109+
// * Memories mostly affect how big the virtual address space
110+
// reservation is for the pooling allocator. Memories require
111+
// ~4G of virtual address space meaning that we can run out
112+
// pretty quickly.
113+
//
114+
// * Tables are not as costly as memories in terms of virtual
115+
// memory and mostly just need to be in the same order of
116+
// magnitude to run that many components.
117+
//
118+
// * Core instances do not have a virtual memory reservation at
119+
// this time, it's just a counter to cap the maximum amount of
120+
// memory allocated (multiplied by `max_core_instance_size`
121+
// below) so the limit is more liberal.
122+
//
123+
// * Table elements limit the maximum size of any allocated
124+
// table, so it's set generously large. This does affect
125+
// virtual memory reservation but it's just 8 bytes per table
126+
// slot.
127+
.total_component_instances(env("SPIN_WASMTIME_INSTANCE_COUNT", max_instances))
128+
.total_memories(env("SPIN_WASMTIME_TOTAL_MEMORIES", max_instances))
129+
.total_tables(env("SPIN_WASMTIME_TOTAL_TABLES", 2 * max_instances))
130+
.total_stacks(env("SPIN_WASMTIME_TOTAL_STACKS", max_instances))
131+
.total_core_instances(env("SPIN_WASMTIME_TOTAL_CORE_INSTANCES", 4 * max_instances))
132+
.table_elements(env("SPIN_WASMTIME_INSTANCE_TABLE_ELEMENTS", 100_000))
96133
// This number accounts for internal data structures that Wasmtime allocates for each instance.
97134
// Instance allocation is proportional to the number of "things" in a wasm module like functions,
98135
// globals, memories, etc. Instance allocations are relatively small and are largely inconsequential
99136
// compared to other runtime state, but a number needs to be chosen here so a relatively large threshold
100137
// of 10MB is arbitrarily chosen. It should be unlikely that any reasonably-sized module hits this limit.
101138
.max_component_instance_size(env("SPIN_WASMTIME_INSTANCE_SIZE", 10 * MB) as usize)
102139
.max_core_instance_size(env("SPIN_WASMTIME_CORE_INSTANCE_SIZE", 10 * MB) as usize)
140+
// Configuration knobs for hard limits per-component for various
141+
// items that require allocations. Note that these are
142+
// per-component limits and instantiating a component still has
143+
// to fit into the `total_*` limits above at runtime.
144+
//
145+
// * Core instances are more or less a reflection of how many
146+
// nested components can be in a component (e.g. via
147+
// composition)
148+
// * The number of memories an instance can have effectively
149+
// limits the number of inner components a composed component
150+
// can have (since each inner component has its own memory).
151+
// We default to 32 for now, and we'll see how often this
152+
// limit gets reached.
153+
// * Tables here are roughly similar to memories but are set a
154+
// bit higher as it's more likely to have more tables than
155+
// memories in a component.
103156
.max_core_instances_per_component(env("SPIN_WASMTIME_CORE_INSTANCE_COUNT", 200))
104-
.max_tables_per_component(env("SPIN_WASMTIME_INSTANCE_TABLES", 20))
105-
.table_elements(env("SPIN_WASMTIME_INSTANCE_TABLE_ELEMENTS", 100_000))
106-
// The number of memories an instance can have effectively limits the number of inner components
107-
// a composed component can have (since each inner component has its own memory). We default to 32 for now, and
108-
// we'll see how often this limit gets reached.
157+
.max_tables_per_component(env("SPIN_WASMTIME_INSTANCE_TABLES", 64))
109158
.max_memories_per_component(env("SPIN_WASMTIME_INSTANCE_MEMORIES", 32))
110-
.total_memories(env("SPIN_WASMTIME_TOTAL_MEMORIES", 1_000))
111-
.total_tables(env("SPIN_WASMTIME_TOTAL_TABLES", 2_000))
159+
// Similar knobs as above, but as specified per-module instead
160+
// of per-component. Note that these limits are much lower as
161+
// core modules typically only have one of each.
162+
.max_tables_per_module(env("SPIN_WASMTIME_MAX_TABLES_PER_MODULE", 2))
163+
.max_memories_per_module(env("SPIN_WASMTIME_MAX_MEMORIES_PER_MODULE", 2))
112164
// Nothing is lost from allowing the maximum size of memory for
113165
// all instance as it's still limited through other the normal
114166
// `StoreLimitsAsync` accounting method too.

0 commit comments

Comments
 (0)