Conversation
Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
This reverts commit 43f08d7.
Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
serban300
left a comment
There was a problem hiding this comment.
Thank you for the suggestion and for opening the PR ! The high level idea looks good ! I just left a couple of comments.
Also could you please let me know how you would prefer to move forward from here ? Would you have time to continue this PR ? If not I would be happy to take it since we need it for the Bridges + XCM.
| } | ||
|
|
||
| // Mark vec as MemLimited since we track the allocated memory: | ||
| impl<T: Decode> crate::memory_limit::DecodeMemLimit for Vec<T> { } |
There was a problem hiding this comment.
I think we need to do impl<T: crate::memory_limit::DecodeMemLimit> crate::memory_limit::DecodeMemLimit for Vec<T> { } . Otherwise I can call decode_with_mem_limit for example on a Vec<Box<_>> which shouldn't support this yet.
|
|
||
| impl MemLimit { | ||
| /// Try to allocate a contiguous chunk of memory. | ||
| pub fn try_alloc(&mut self, size: usize) -> Result<(), Error> { |
There was a problem hiding this comment.
I would rather just provide the type and do std::mem::size_of() here which should account for the alignment automatically.
| fn ascend_ref(&mut self) {} | ||
|
|
||
| /// Try to allocate a contiguous chunk of memory of `size` bytes. | ||
| fn try_alloc(&mut self, size: usize) -> Result<(), Error> { |
There was a problem hiding this comment.
Nit: It looks like we won't really allocate memory here, but only keep track of it. So I would rename this to something like note_alloc(). Or even something more generic like on_before_decode()
| /// | ||
| /// Should be used as a marker for types that do the memory tracking in their `decode` implementation with [`Input::try_alloc`]. | ||
| pub trait DecodeMemLimit: Decode + Sized { | ||
| fn decode_with_mem_limit<I: Input>(limit: MemLimit, input: &mut I) -> Result<Self, Error> { |
There was a problem hiding this comment.
This approach seems a bit limiting since we can only do decode_with_mem_limit or decode_with_depth_limit. Looks like we can't combine them. It would be nice to be able to do something more generic. For example something like:
decode_with_check(input: &mut I, check: FnMut(DecodeContext) -> Result<(), Error>)
where DecodeContext would be something like
DecodeContext {
depth: u32,
used_memory: usize,
...
}
This way we could check both for depth and memory limit at the same time. Maybe for other parameters as well. Also would be nice to be able to keep track of the number of objects of a certain type that we encounter recursively while decoding since this is also something that we needed in Polkadot.
There was a problem hiding this comment.
Looks like we can't combine them.
We can. Which is why i chose this approach. These functions are just wrappers, but there is nobody stopping you from doing:
let mut input = MemTrackingInput { inner: DepthTrackingInput { inner: .. }};
T::decode(&mut input);But it wont have any meaning without requiring T: DecodeMemLimit.
This way we could check both for depth and memory limit at the same time. Maybe for other parameters as well. Also would be nice to be able to keep track of the number of objects of a certain type that we encounter recursively while decoding since this is also something that we needed in Polkadot.
Yea, i had some decode_with_context<C>(..) implemented before, but it makes this not such an opt-in.
For example when some type only wants to track recursion and another only size. If we otherwise enforce this, it would be a big breaking change.
The breaking change would occur in the case where we make the Parameter type of a FRAME extrinsic require this.
| /// the most fitting name. | ||
| pub enum MemAlignment { | ||
| /// Round up to the next power of two. | ||
| NextPowerOfTwo, |
There was a problem hiding this comment.
Note: Currently when using this with a Vector, we align each element individually.
But a Vec allocates all elements in a chunk through the WASM bump allocator. So what we have to do is round(sum(e...)) over all elements instead of sum(round(e)..).
Otherwise it would round each element size up to the next power of two, but the vector may still allocate more since it aligns with the sum of the sizes of all elements.
|
I've been thinking about this some more and I'm not sure if this solution is feasible. My main concern is related to enums. For example, for the following enum:
I don't know if we can check the type of each field in an enum variant and call Another concern is that for structures like Another problem would be if we have skipped fields that consume significant memory. For example a Another issue is that here we do Also, since I was looking on these, I think there are some issues with the
I'm not sure. Maybe I'm missing something. @ggwpez , @bkchr what do you think ? |
We'll limit this to tracking only heap size, but we want to make it type safe. Tracked as part of: #609 Will close this draft for the moment and will open a separate PR later |
Adds a
try_allocfunction to theInputtrait to track allocations. This makes it necessary to implement it in thedecodeany type, but is not type checked. Currently it is only implemented for vectors.Some tests are in place to showcase the usage.
This MR adds functionality to restrict the memory usage while decoding a type.Some tests show how to use it with nested types or tuples, but mostly a PoC to evaluate the approach.
We could also do it like the
DecodeLimitand track the memory consumption in theInputstream, but it cannot be enforced by the type system.PS: I will change it to track the limit in the
Input, as it allows to also track the other limits (like recursion depth) at the expense of not being type checked.Changes
Adds a generic
DecodeWithContextthat allows adecode_with_contextcall to pass down some context when decoding.This new trait is then used together withMemLimitsuch thatVecimplementsDecodeWithContext<MemLimit>.There is a blanket in place that converts
DecodeWithContext<MemLimit>toDecodeWithMemLimit, and therebyte,Vecis nowDecodeWithMemLimit.