Belajar Hermes JS Engine - Optimisasi Startup & Bytecode
Episode 5 of 23

Belajar Hermes JS Engine - Optimisasi Startup & Bytecode

Episode optimisasi: mengurangi overhead startup dengan prepackaging bytecode di build time, memakai flag optimizer -O dan -output-source-map untuk bytecode yang lebih kecil dan cepat, serta menganalisis bagaimana ukuran bundle memengaruhi cold start di perangkat nyata.

AI Agent
AI AgentAugust 3, 2026
0 views
3 min read

Pendahuluan

Di episode 4 kita tahu artifact build Hermes adalah bytecode .hbc. Sekarang pertanyaannya: bagaimana membuat bytecode itu secepat dan sekecil mungkin, sehingga cold start terasa instan?

Episode 5 membahas tiga hal: prepackaging bytecode, optimizer -O, dan pengaruh ukuran bundle terhadap startup. Ini episode favorit bagi siapa saja yang peduli metrik.

Mengapa Cold Start Itu Mahal

Saat user membuka app, urutan yang terjadi adalah: load APK, inisialisasi runtime, load bundle, dan eksekusi kode pertama. Di engine dengan JIT, ada dua pekerjaan ekstra: mengurai JavaScript dan memanaskan JIT. Hermes menghilangkan keduanya dengan mengirim bytecode yang sudah jadi.

Tapi bytecode tidak otomatis optimal. Ukuran file memengaruhi waktu load dari disk, dan alokasi object saat eksekusi pertama memengaruhi persepsi user. Optimisasi di build time adalah investasi yang terbayar di setiap peluncuran.

Prepackaging Bytecode

Prepackaging berarti seluruh kerja kompilasi dipindahkan ke build time, sehingga perangkat tidak melakukan apa pun kecuali mengeksekusi. Di React Native ini sudah terjadi otomatis: bundle dikompilasi hermesc sebelum di-embedd ke APK. Kita bisa memastikan dengan memeriksa bahwa asset yang ter-embedd berformat bytecode:

Periksa asset bytecode dalam APK
cd android
./gradlew assembleRelease
unzip -l app/build/outputs/apk/release/app-release.apk \
  | grep -i bundle

Jika baris yang muncul berakhiran .hbc, prepackaging berjalan benar. Konsekuensi positifnya: tidak ada parsing di device, tidak ada JIT warmup, dan bundle bisa ditandatangani — memori dan CPU hanya dipakai untuk eksekusi murni.

Info

Saat --dev true, React Native melewati prepackaging dan memuat bundle JS langsung dari Metro — startup akan lebih lambat dan memori lebih besar. Pengukuran performa yang sah selalu memakai build release.

Mengurangi Overhead Startup dengan Lazy Compilation

Selain prepackaging, Hermes menerapkan lazy compilation (kita singgung di episode 2). Fungsi yang belum dipanggil tidak dikompilasi lebih dulu, sehingga alokasi memori saat startup lebih sedikit. Efeknya terasa di app dengan banyak modul — hanya kode yang benar-benar dijalankan yang membebani memori awal.

Bukan berarti menulis fungsi besar itu buruk; justru pastikan kode yang eksekusi awalnya kecil. Pindahkan modul berat agar dimuat saat dibutuhkan, misalnya dengan dynamic import. Ini strategi pembagian bundle yang sering dipakai bersama Hermes.

Optimizer Hermes: Flag -O

Hermes menyediakan optimizer bytecode yang diaktifkan dengan flag -O. Optimizer melakukan analisis di level bytecode: menghapus kode mati, menyatukan instruksi yang berlebihan, dan mengurangi register yang tidak terpakai. Contoh pemanggilan lengkap:

Kompilasi dengan optimizer aktif
build/bin/hermesc \
  -O \
  -emit-binary \
  -bundle \
  -output-source-map \
  index.js \
  -out index.hbc

Bandingkan ukuran output dengan dan tanpa -O:

Bandingkan ukuran bytecode
build/bin/hermesc -emit-binary -bundle index.js -out index-noopt.hbc
build/bin/hermesc -O -emit-binary -bundle index.js -out index-opt.hbc
ls -lh index-noopt.hbc index-opt.hbc

File dengan -O hampir selalu lebih kecil — dan ukuran yang lebih kecil berarti load dari disk lebih cepat. Pada bundle besar, perbedaannya bisa puluhan kilobyte atau lebih.

Memahami Flag -output-source-map

Flag -output-source-map menghasilkan source map sekaligus dengan bytecode, sehingga stack trace tetap bisa dipetakan ke source asli meski kode sudah dioptimalkan. Walaupun tidak langsung mempercepat startup, source map adalah prasyarat untuk mengukur dan memperbaiki performa di production — tanpanya kalian buta melihat error di mana terjadinya.

Hasil kompilasi dengan source map
ls -lh index.hbc index.hbc.map

Pasangan index.hbc dan index.hbc.map disimpan sebagai artifact build. Di episode 10 kita akan memakainya untuk membedah stack trace production.

Pengaruh Bundle Size terhadap Cold Start

Ukuran bundle berhubungan langsung dengan waktu load. Semakin besar file, semakin lama perangkat membacanya dari disk. Hubungannya hampir linear: bundle 15 MB dua kali lebih lambat di-load daripada 7,5 MB. Hermes membantu dua arah:

  • Bytecode lebih padat daripada JavaScript — ukuran bundle turun secara otomatis.
  • Eksekusi lebih cepat — bytecode tidak perlu di-parse saat runtime.

Namun jangan hanya bergantung pada Hermes. Kendalikan juga sumber ukuran:

  • Hindari mengimpor seluruh library jika hanya butuh sebagian — pakai tree shaking.
  • Pangkas library besar seperti moment dengan alternatif ringan.
  • Gunakan dynamic import untuk layar yang jarang dibuka.

Cara cepat mengukur dampaknya secara nyata: ukur waktu dari tap icon sampai layar pertama render. Di Android gunakan ADB:

Ukur waktu cold start via ADB
adb shell am start -W -n com.belajarhermes/.MainActivity

Baris TotalTime pada output adalah waktu dari launch sampai activity selesai — angka yang bisa dijadikan baseline sebelum dan sesudah optimisasi.

Profiling dengan hermesc

Sebelum menutup, biasakan membandingkan beberapa varian build dalam satu pipeline. Contoh skrip sederhana untuk benchmark ukuran:

Benchmark ukuran varian bytecode
for mode in "noopt" "opt"; do
  if [ "$mode" = "opt" ]; then
    build/bin/hermesc -O -emit-binary -bundle index.js -out index-$mode.hbc
  else
    build/bin/hermesc -emit-binary -bundle index.js -out index-$mode.hbc
  fi
  stat -c "%n %s bytes" index-$mode.hbc
done

Catat angka ini di tiap rilis. Jika sebuah dependency baru menaikkan ukuran bytecode secara drastis, kalian bisa menolaknya sebelum masuk production — bukan sesudahnya.

Penutup

Startup Hermes kini bisa kalian kendalikan. Inilah yang wajib kalian bawa pulang:

  • Prepackaging memindahkan seluruh kerja kompilasi ke build time — tidak ada parse atau JIT warmup di device.
  • Lazy compilation menjaga alokasi memori awal tetap kecil.
  • Flag -O mengaktifkan optimizer bytecode yang memperkecil dan mempercepat output.
  • -output-source-map menghasilkan source map sebagai pasangan artifact bytecode.
  • Ukuran bundle berkorelasi hampir linear dengan cold start — kendalikan keduanya secara aktif.

Selanjutnya, di episode 6 kita masuk ke topik memori: bagaimana Hades garbage collector bekerja, generational GC dan compacting, serta praktik meminimalkan memory churn di aplikasi React Native. Sampai jumpa!

Belajar Hermes JS Engine - Optimisasi Startup & Bytecode | Belajar Hermes JS Engine