Contoh Program
Sekarang mari kita lihat bagaimana register (r0-r10), region memori, dan instruksi bekerja bersama dalam program yang sebenarnya. Kita akan mulai dengan program sBPF paling sederhana untuk memahami alur eksekusi fundamental.
NoOp-program
Program "NoOp" (No Operation) sangat cocok untuk pembelajaran karena menunjukkan struktur program yang esensial tanpa kerumitan:
- Bagaimana program menerima input (dalam register r1)
- Bagaimana program mengembalikan hasil (dalam register r0)
- Alur masuk/keluar dasar yang diikuti setiap program sBPF
- Bagaimana Rust dikompilasi menjadi assembly sBPF
Meskipun program ini "tidak melakukan apa-apa," program ini menunjukkan dasar yang menjadi landasan setiap program Solana.
Sekarang kita telah mengetahui operasi dasar sBPF, mari kita lihat seperti apa operasi tersebut dalam program nyata (meskipun sangat kecil).
Pinocchio NoOp
Di bawah ini adalah "noop" berkinerja tinggi yang ditulis dengan Pinocchio. Yang dilakukannya hanyalah mengembalikan keberhasilan:
#![no_std]
use pinocchio::{entrypoint::InstructionContext, lazy_program_entrypoint, no_allocator, nostd_panic_handler, ProgramResult};
lazy_program_entrypoint!(process_instruction);
nostd_panic_handler!();
no_allocator!();
fn process_instruction(
_context: InstructionContext, // wrapper around the input buffer
) -> ProgramResult {
Ok(())
}
Jika kita membangun kode ini dengan cargo build-sbf --dump
, kita akan mendapatkan dump ELF yang memberikan informasi tentang binary kita di direktori /target/deploy/
.
Kemudian kita perlu mencari bagian .text
— bagian dari binary kita tempat kode yang dapat dieksekusi disimpan.
Disassembly of section .text
0000000000000120 <entrypoint>
120 b7 00 00 00 00 00 00 00 mov64 r0, 0x0
128 95 00 00 00 00 00 00 00 exit
Mari kita uraikan apa arti byte-byte heksadesimal ini menggunakan format instruksi yang telah kita pelajari:
Ini adalah instruksi pertama: 120 b7 00 00 00 00 00 00 00
- Alamat:
0x120
(dalam region teks yang dimulai pada 0x100000000) - Opcode:
0xb7
=mov64
dengan nilai immediate - dst:
0x00
= registerr0
- src:
0x00
= tidak digunakan (operasi immediate) - offset:
0x0000
= tidak digunakan - imm:
0x00000000
= nilai immediate 0
Dan ini adalah instruksi kedua: 128 95 00 00 00 00 00 00 00
- Alamat:
0x128
(8 byte setelah instruksi pertama) - Opcode:
0x95
= exit/return - Semua field lainnya:
0x00
= tidak digunakan untuk exit
Assembly NoOp
Jika kita mendisassembly binary untuk mengubahnya kembali menjadi sBPF Assembly yang dapat dikompilasi, kodenya akan terlihat seperti ini:
.globl entrypoint
entrypoint:
mov64 r0, 0x00 // r0 <- success
exit // finish, return r0
Mari kita uraikan kodenya:
.globl entrypoint
: Ini adalah direktif assembler yang memberi tahu linker untuk membuat simbol entrypoint terlihat secara global. Runtime Solana mencari simbol ini untuk mengetahui di mana harus mulai mengeksekusi program Anda.entrypoint
: Ini adalah label yang menandai alamat memori di mana eksekusi program dimulai. Ketika runtime memuat program Anda, ia melompat ke alamat ini.mov64 r0, 0x00
: Memindahkan nilai langsung 0 ke register r0. Karena r0 adalah register return, ini menyiapkan kode return sukses.exit
: Mengakhiri eksekusi program dan mengembalikan nilai dalam r0 ke runtime.
Ini adalah program yang sangat kecil, dengan hanya 2 instruksi yang mengkonsumsi hanya 2 compute unit (CU) untuk dieksekusi yang memetakan dengan sempurna ke kode Rust kita:
- Kita mendefinisikan fungsi entrypoint
- Kita mengembalikan Ok(()) yang dievaluasi menjadi 0
- Compiler menghasilkan instruksi
mov64
danexit
yang sesuai
Optimized Assembly NoOp
Namun, kita dapat mengoptimalkan ini lebih lanjut. Karena runtime Solana menginisialisasi r0
ke 0 secara default, kita dapat menghilangkan instruksi mov64
yang redundan:
.globl entrypoint
entrypoint:
exit
Versi yang dioptimalkan ini:
- Hanya membutuhkan 1 unit komputasi (pengurangan 50%!)
- Menghasilkan hasil yang identik (
r0
masih berisi 0)
Optimasi ini dimungkinkan karena kita mengetahui status register awal — sesuatu yang tidak selalu dimanfaatkan oleh compiler Rust. Memahami assembly sBPF memungkinkan Anda mengidentifikasi dan menghilangkan inefisiensi seperti ini dalam kode yang kritis terhadap performa.