Batch Instructions
Cross-Program Invocations (CPIs) chi tiêu một chi phí cơ bản là 1,000 đơn vị tính toán cho mỗi lời gọi. Đối với các chương trình nhận được nhiều CPI thường xuyên trong cùng một instruction, chi phí này trở thành một nút thắt cổ chai hiệu suất đáng kể.
Để giải quyết vấn đề không hiệu quả này, Dean đã tạo ra "batch" instruction trong PR này cho p-token, cho phép nhiều hoạt động được thực hiện trong một cuộc gọi CPI duy nhất.
Batch Instruction
Một batch instruction cho phép nhiều hoạt động được xử lý trong một CPI duy nhất thay vì yêu cầu các cuộc gọi riêng biệt cho mỗi hoạt động. Điều này làm giảm đáng kể mức tiêu thụ đơn vị tính toán cho các chương trình xử lý nhiều hoạt động liên quan.
Cấu trúc
Batch instruction sử dụng một cấu trúc header nâng cao chứa:
Số lượng tài khoản: Số lượng tài khoản cần thiết cho instruction bên trong
Độ dài dữ liệu: Kích thước của dữ liệu instruction
Phần header này cho phép xử lý hiệu quả nhiều "inner" instructions trong một batch duy nhất. Hệ thống sẽ lặp qua và xử lý từng inner instruction một cách tuần tự.
Thiết kế entrypoint
Entrypoint đầu tiên kiểm tra discriminator instruction để xác định xem có nên xử lý một batch instruction hay một regular instruction. Điều này ngăn chặn việc lồng ghép các batch instructions trong các batch instructions khác, điều này sẽ không hợp lệ.
Như thế này:
#[inline(always)]
pub fn process_instruction(
_program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8],
) -> ProgramResult {
let [discriminator, remaining @ ..] = instruction_data else {
return Err(TokenError::InvalidInstruction.into());
};
let result = if *discriminator == 255 {
// 255 - Batch
#[cfg(feature = "logging")]
pinocchio::msg!("Instruction: Batch");
process_batch(accounts, remaining)
} else {
inner_process_instruction(accounts, instruction_data)
};
result.inspect_err(log_error)
}Sử lý Batch
Hàm process_batch xử lý logic chính của việc xử lý batch:
/// The size of the batch instruction header.
///
/// The header of each instruction consists of two `u8` values:
/// * number of the accounts
/// * length of the instruction data
const IX_HEADER_SIZE: usize = 2;
pub fn process_batch(mut accounts: &[AccountInfo], mut instruction_data: &[u8]) -> ProgramResult {
loop {
// Validates the instruction data and accounts offset.
if instruction_data.len() < IX_HEADER_SIZE {
// The instruction data must have at least two bytes.
return Err(TokenError::InvalidInstruction.into());
}
// SAFETY: The instruction data is guaranteed to have at least two bytes
// (header) + one byte (discriminator) and the values are within the bounds
// of an `usize`.
let expected_accounts = unsafe { *instruction_data.get_unchecked(0) as usize };
let data_offset = IX_HEADER_SIZE + unsafe { *instruction_data.get_unchecked(1) as usize };
if instruction_data.len() < data_offset || data_offset == IX_HEADER_SIZE {
return Err(TokenError::InvalidInstruction.into());
}
if accounts.len() < expected_accounts {
return Err(ProgramError::NotEnoughAccountKeys);
}
// Process the instruction.
// SAFETY: The instruction data and accounts lengths are already validated so
// all slices are guaranteed to be valid.
let (ix_accounts, ix_data) = unsafe {
(
accounts.get_unchecked(..expected_accounts),
instruction_data.get_unchecked(IX_HEADER_SIZE..data_offset),
)
};
inner_process_instruction(ix_accounts, ix_data)?;
if data_offset == instruction_data.len() {
// The batch is complete.
break;
}
accounts = &accounts[expected_accounts..];
instruction_data = &instruction_data[data_offset..];
}
Ok(())
}Đây là những gì xảy ra trong hàm này:
Header Validation: Hàm bắt đầu bằng cách xác thực rằng dữ liệu instruction chứa ít nhất kích thước header yêu cầu (2 byte).
Account and Data Extraction: Nó trích xuất số lượng tài khoản mong đợi và tính toán offset dữ liệu, xác thực các giá trị này để ngăn chặn hành vi không xác định.
Instruction Processing: Mỗi instruction bên trong được xử lý bằng cách sử dụng hàm
inner_process_instructiontiêu chuẩn với các tài khoản và dữ liệu cụ thể của nó.Loop Control: Hàm tiếp tục xử lý cho đến khi tất cả các instruction trong batch hoàn tất, tiến tới các con trỏ tài khoản và dữ liệu instruction cho mỗi lần lặp.