-
Notifications
You must be signed in to change notification settings - Fork 71
Implementation of feat109: add a trait to serialize instruction data #131
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
base: main
Are you sure you want to change the base?
Conversation
// Instruction data layout: | ||
// - [0]: instruction discriminator (1 byte, u8) | ||
// - [1..9]: amount (8 bytes, u64) | ||
let mut instruction_data = Box::new([UNINIT_BYTE; 9]); |
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.
We need to avoid using a Box
here since it will be expensive. You can use: core::mem::MaybeUninit<[u8; 9]>
.
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.
there will be a lifetime issue using stack alone. one way to do this is by specifying this function #[inline(always)]
so that the variable will always be on the same stack with the caller. but this is too hacky wdyt?
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.
assuming using the bump allocator, allocating a fixed size may only increment the free pointer, like 2 ebpf instructions?
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.
Based on some quick tests that I did, using a Box
is a bit more expensive (#126) than just the pointer increment.
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.
We can use a similar approach than the ReturnData
: https://github.com/anza-xyz/pinocchio/blob/main/sdk/pinocchio/src/cpi.rs#L316-L325
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.
assuming using the bump allocator, allocating a fixed size may only increment the free pointer, like 2 ebpf instructions?
this is interesting. one overhead in the example would be zero initialization. i cannot think of any other operation a simple alloc would do. can verify this later.
update: confirmed. beside zero initialization, alignment stuff and pointer update, the alloc
crate itself emits tons of rust error unwinding like we had in #85. using alloc
crate should be discouraged if we want max performance.
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.
We can use a similar approach than the
ReturnData
: https://github.com/anza-xyz/pinocchio/blob/main/sdk/pinocchio/src/cpi.rs#L316-L325
In the example, seems only get_return_data generates the data, are you suggesting that we do not parse pointer around, instead just return [u8] back?
If we want to parse pointer around, the data has to live on the heap somewhere.
// Set amount as u64 at offset [1..9] | ||
write_bytes(&mut instruction_data[1..], &self.amount.to_le_bytes()); | ||
|
||
unsafe { from_raw_parts(instruction_data.as_ptr() as _, 9) } |
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.
Then here you can have unsafe { instruction_data.assume_init() }
.
@@ -15,3 +15,7 @@ fn write_bytes(destination: &mut [MaybeUninit<u8>], source: &[u8]) { | |||
d.write(*s); | |||
} | |||
} | |||
|
|||
pub trait InstructionData { | |||
fn get_instruction_data(&self) -> &[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.
Maybe we can name this get
.
This implements #109 for Token program
If things look good I will implement this for all other programs.
I am not using Deref because it requires to implement Deref for all Struct like ApproveChecked/Burn/FreezeAccount
"pub trait InstructionData: Deref<Target = [u8]> {}"