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

Reduce wasmi::Engine memory consumption for storing function artifacts #982

Closed
Robbepop opened this issue Apr 16, 2024 · 3 comments · Fixed by #990
Closed

Reduce wasmi::Engine memory consumption for storing function artifacts #982

Robbepop opened this issue Apr 16, 2024 · 3 comments · Fixed by #990

Comments

@Robbepop
Copy link
Member

Robbepop commented Apr 16, 2024

The wasmi::Engine stores the compiled functions and for lazy compilation also the information necessary for lazy compilation for each function in parsed wasmi::Modules. However, the memory consumption is non-optimal and could easily be improved.

pub struct UncompiledFuncEntity {

The root cause is that each UncompiledFuncEntity stores redundant information:

  • The func_to_validate: FuncToValidate<..> field internally stores the func_idx which could be eliminated. The new version of wasmparser allows to destructure the FuncToValidate type to do this.
  • Also the FuncToValidate type holds a WasmFeatures which currently weighs in with a whopping 23 bytes. Again, the new version allows to extract this WasmFeatures and store it once shared for all uncompiled function entities.

With these two changes we can eliminate roughly 32 bytes (alignment) per Wasm function in the wasmi::Engine. For a Wasm blob with 1000 functions this equals to roughly 32kB less memory consumption which can be big. In total this would shrink the UncompiledFuncEntity type to 20 bytes on a 64-bit platform or 12 bytes on a 32-bit platform.

Also the CompiledFunctionEntity type can be shrinked:

pub struct CompiledFuncEntity {

We could store raw-pointers instead of Box<[T]> for both instrs and consts fields and store the lengths in u16 fields instead. On 64-bit platforms this would shrink the type from 34 bytes (40 bytes aligned) down to 22 bytes (24 bytes aligned). Additionally this would make the len_cells method a lot more efficient.


This could have a big impact especially on Wasmi's new lazy compilation functionality when a malicious attacker uses a Wasm with tons of (near) empty functions in order to stretch the worst-case for lazy Wasmi's compilation.

@Robbepop
Copy link
Member Author

@Robbepop
Copy link
Member Author

Robbepop commented Apr 17, 2024

Using the following definitions for Wasmi's code_map.rs:

pub struct UncompiledFuncEntity {
    func_index: u32,
    bytes: SmallByteSlice,
    module: ModuleHeader,
    validate: Option<wasmparser::ValidatorResources>,
}

pub enum SmallByteSlice {
    Small {
        len: u8,
        bytes: [u8; 22],
    },
    Big(Box<[u8]>),
}
  • SmallByteSlice now stores 22 bytes inline instead of 30.
    • Note: With union and unsafe Rust trickery we could decrease the size of this type by another 8 bytes, however, for now I don't think the gains are worth this refactor.
  • Replaced func_to_validate: Option<wasmparser::FuncToValidate<wasmparser::ValidatorResources>> field with validate: Option<wasmparser::ValidatorResources> which is a lot smaller. The index and ty fields are redundantly stored in UncompiledFuncEntity::func_index field with access to module if we do some refactoring in ModuleHeader. Furthermore the features: WasmFeatures field is the same for all uncompiled function entities in the same Engine and thus can be stored in the CodeMap directly and reused.

All in all this nets us the following improvements in memory consumption:

  • size_of<UncompiledFuncEntity>(): 88 -> 48
  • size_of<InternalFuncEntity>(): 88 -> 48

Therefore, 40 bytes savings per function processed by the Engine.
For 1000 functions this is equivalent to 40kB.

@Robbepop
Copy link
Member Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant