Menjalankan model bahasa besar (LLM) di ponsel bukan lagi sekadar eksperimen penelitian. Ini sudah menjadi realitas produk yang siap dirilis. Namun, saat Anda beralih dari demo mainan ke produk nyata, latensi kembali muncul. Anda telah memangkas model, melakukan kuantisasi pada bobot, namun fase prefill tetap terasa lambat. Token tertahan. UI membeku. Kesalahan jarang ditujukan ke tempat yang seharusnya.

Pada perangkat Android, komputasi hampir tidak pernah menjadi hambatan (bottleneck) selama fase prefill LLM. Hambatannya adalah bandwidth memori. SoC flagship modern dilengkapi dengan inti GPU dan NPU yang kuat yang dapat memproses aritmatika jauh lebih cepat daripada kemampuan subsistem memori untuk menyediakannya. Saat Anda melakukan profiling pada implementasi attention yang naif, unit eksekusi tidak jenuh. Mereka sedang menunggu. Menunggu DRAM.

Mengapa Model Terkuantisasi Anda Masih Terasa Lambat

Kuantisasi telah menjadi langkah pertama standar untuk inferensi pada perangkat (on-device inference). Memperkecil bobot dari FP16 ke INT8 memangkas ukuran model menjadi setengahnya dan mengurangi penyimpanan. Ini membantu. Namun, hal ini tidak memperbaiki latensi pada lapisan attention. Alasannya sederhana: kuantisasi mengurangi jumlah data yang Anda simpan, tetapi tidak mengurangi jumlah transaksi memori yang dilakukan oleh mekanisme attention.

Lapisan multi-head attention standar, jika diimplementasikan sesuai buku teks, melakukan tiga kali perjalanan bolak-balik penuh ke DRAM untuk setiap lapisan. Matriks Query, Key, dan Value dibaca dari memori utama, skor dihitung, dan hasil perantara ditulis kembali. Aritmatikanya sepele. Pergerakan datanya sangat berat. Di Android, di mana anggaran daya dan termal sangat ketat, pola ini membebani bus memori secara berlebihan. Prosesor secara efektif membayar tol tiga kali untuk menyeberangi jembatan yang sama.

Jika Anda telah merilis model INT8 dan bertanya-tanya mengapa langkah prefill masih berskala kuadratik terhadap panjang prompt, inilah jawabannya. Bobotnya lebih kecil, tetapi lalu lintas aktivasi tetap sangat besar.

Hambatannya Adalah Memori, Bukan Matematika

Untuk memahami solusinya, lihatlah roofline. GPU dan NPU seluler pada chip seperti Snapdragon 8 Gen 3 dan Dimensity 9300 memiliki throughput komputasi teoretis yang jauh melampaui apa yang dapat ditopang oleh antarmuka LPDDR5X mereka. Dalam kernel attention yang naif, setiap head menghitung softmax atas hasil perkalian Q dan K, lalu mengalikannya dengan V. Setiap matriks skor perantara dimaterialisasi dalam memori global. Itu berarti pembacaan DRAM puncak Anda berskala dengan kuadrat dari panjang urutan, atau O(n²). Untuk prompt 1024 token, lalu lintas memori sudah cukup besar untuk mendominasi waktu eksekusi.

Inti-inti prosesor tidak dimanfaatkan secara maksimal karena tidak dapat menyembunyikan latensi. Prosesor modern mengandalkan cache untuk menjaga pipeline tetap penuh. Ketika sebuah algoritma terus-menerus mengalami cache miss dan mengambil data dari DRAM, unit eksekusi akan menganggur. Seberapa banyak pun kuantisasi yang dilakukan tidak akan memperbaiki ketidaksesuaian struktural antara kapasitas komputasi dan pasokan memori ini.

Bagaimana Tiling Mengambil Kembali Bandwidth

Solusinya adalah strategi tiling yang menjaga skor perantara di dalam SRAM on-chip alih-alih mengirimkannya ke DRAM. Ini adalah wawasan yang sama yang mendorong Flash Attention, yang diadaptasi untuk tumpukan komputasi (compute stack) Android. Alih-alih mematerialisasi matriks skor n × n yang penuh di memori, Anda memecah komputasi menjadi tile kecil yang muat di dalam cache L1. Anda menghitung statistik softmax lokal, mengakumulasi nilai max berjalan dan jumlah normalisasi, dan hanya menulis hasil berbobot akhir kembali ke memori.

Ini mengubah kompleksitas bandwidth. Pembacaan DRAM puncak turun dari O(n²) menjadi O(n), karena Anda tidak lagi perlu mengangkut matriks skor penuh melalui memori utama. Pekerjaan berat terjadi di dalam SRAM, tepat di samping unit eksekusi.

Sebagai contoh konkret, pertimbangkan ukuran tile sebesar 64 dan dimensi head sebesar 128. Tile skor tersebut memakan ruang 16 KB. Jejak memori tersebut muat dengan nyaman di dalam cache L1 SoC flagship saat ini seperti Snapdragon 8 Gen 3 dan Dimensity 9300. Aritmatikanya tetap lokal. Bus memori pun bisa "bernapas".

Mengimplementasikan Ini di Android

Sketsa algoritmanya sederhana, meskipun memastikan detailnya benar sangatlah penting.

Pecah Query, Key Anda