-
Notifications
You must be signed in to change notification settings - Fork 2.7k
improve runtime call, and its usage #9385
Conversation
|
thinking again maybe we should introduce something similar to |
| impl<T: Config> Pallet<T> { | ||
| /// The limit on the number of batched calls. | ||
| fn batched_calls_limit() -> u32 { | ||
| let allocator_limit = 33554432; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| let allocator_limit = 33554432; | |
| let allocator_limit = 33554432; |
feels this number should come from somewhere else?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
indeed but we can't currently link to it because it is in the client.
Maybe the proper solution is to have a configuratble limit: type MaxBatched: Get<u32>, or move the allocator limit to the primitives
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe better to set a hardcoded limit of 10_000 and then assert this in a test
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| origin: OriginFor<T>, | ||
| source: <T::Lookup as StaticLookup>::Source, | ||
| dest: <T::Lookup as StaticLookup>::Source, | ||
| source: Box<<T::Lookup as StaticLookup>::Source>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
curious: what is the static size of this? a bit surprised that it is part of the offending ones.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it is 40 bytes if I remember correctly, note that an account id is already 32 bytes
kianenigma
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't check all, but looks mostly good to me.
Would be good if we add some guidelines around this. Maybe you can compile a pallet with PRINT_CALL_SIZE=1 or similar, and it would report the call size.
This is something that needs to be communicated to devhub IMO.
| code: Box<Vec<u8>>, | ||
| data: Box<Vec<u8>>, | ||
| salt: Box<Vec<u8>>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we box Vec? It already allocates its elements on the heap. There are more places where this happened. I just mark this one as example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was surprised too but a vec is 24 bytes https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=076c59fc288d9fe6bb17e1c7c92e35a7
so this extrinsic variant fields are: 16 + 8 + 24 * 3 added to the variant indices: 1 for pallet variant, 1 for call variant. It is 98 bytes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ohh. I expected it to be much smaller. Then go ahead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it is heap in both cases, but it is more with Vec https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=9f93db0b5c42a9ece4f8658a34d821d0
|
maybe better than this we can just box each pallets call. pub enum RuntimeCall {
PalletCall(Box<PalletCall>),
...
}So that if some pallet call have big size it is not shared with all other pallet calls |
I was about to make exactly this proposition but was unsure if it is possible with those macros. It makes the code really ugly to have Boxes everywhere. Would love to see it implemented in this way transparently for the pallets. Also this would be way less allocation pressure, right? |
|
But the downside of that is that even for small data that are more efficiently managed on the stack, you force them to be on the heap. I recall that in another PR benchmarked showed 100% performance hit (twice larger weight) in boxing stuff. I (unfortunately) think that manual boxing is better to begin with, and over time we improve it if we learn that a certain pattern is best. and again I want to emphasize that some tool to print the size of call enum variants is very important. |
You mean for dispatchables which have small parameters? Yeah that is an obvious drawback. But maybe we can just make this more granular? Instead of boxing per pallet or per argument, boxing per dispatchable (you will opt-in with an attribute) might be the sweet spot? enum RuntimeCall {
CoolPallet(CoolPallet),
}
enum CoolPallet {
BigDispatchable(Box<(BigArg, EvenBiggerArg)>),
SmallDispatchable(u32),
}
mod cool_pallet {
#[weight(1000), box_args]
fn big_dispatchable(big_arg: BigArg, even_bigger_arg: EvenBiggerArg) {}
#[weight(1000)]
fn small_dispatchable(small_arg: u32) {}
} |
| assert_eq!(Asset::<Test>::get(0).unwrap().approvals, 1); | ||
| assert_eq!(Balances::reserved_balance(&1), 1); | ||
| assert_ok!(Assets::transfer_approved(Origin::signed(2), 0, 1, 3, 40)); | ||
| assert_ok!(Assets::transfer_approved(Origin::signed(2), 0, Box::new(1), Box::new(3), 40)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is a bit sad.
Yeah, I also thought that we should do this as a last step, but before maybe fix the individual pallet calls. |
|
I kinda getting why this is needed but is there some proper analysis and explanation of why this is necessary and what should parachain do with their pallets? |
|
I agree with what @athei propose: opt-in boxing of the whole call variant. |
Complexity of the macro you mean? But these are one time costs. Burdening the pallet authors with boxing everything will be payed by more people and more often. |
|
reading everything again, I see two ways to go:
your entire pallet call is boxed when being amalgamated into the upper runtime call. I think a combination of both is fine. If a pallet mostly have large calls, we can box it all. For other pallet, we manually box one or few arguments of individual dispatchables. |
|
@ascjones had a usecase that requires to put all call variant into its own struct, so that generating type_info is easier. If we accept this direction then we can add an opt-in attribute |
|
sorry for the back and forth but Andrew doesn't need to wrap in struct anymore. I think we can go for boxing the pallet calls in the runtime outer call. (We should probably also avoid more than 200 bytes pallet calls, there are a clippy lint for this, but it is less important). |
|
This is the follow-up #9398 |
This reduce the size of runtime call to be less than 90.
It also improve its usage in frame-utility.