Hidup terlalu singkat untuk dipakai menulis di terminal yang lambat
(mijndertstuij.nl)- Kecepatan terminal yang digunakan sepanjang hari sangat menentukan efisiensi kerja, dan jeda kecil saat membuka tab baru, mengetik, atau autocompletion menjadi tidak efisien ketika terakumulasi ratusan kali dalam sehari
- Shell interaktif yang termuat sepenuhnya dapat dijalankan hanya dalam sekitar 30 milidetik meski mencakup autocompletion, syntax highlighting, autosuggestion, fzf, dan direnv, serta tab baru kini terbuka seketika
- Rahasia terbesarnya adalah tidak memakai framework dan plugin manager seperti oh-my-zsh atau prezto; cukup
git clonelangsung 3 plugin lalusourcedari.zshrc - Dengan caching
compinit, lazy-loading, prompt asinkron, terminal berakselerasi GPU, dan lainnya, latensi saat startup, prompt, dan input semuanya diminimalkan - Sebagian besar optimasi bukan soal menambahkan sesuatu, melainkan menyingkirkan hal yang tidak perlu, dan kuncinya adalah disiplin untuk hanya menambahkan yang benar-benar sering dipakai
Mengapa terminal cepat itu penting
- Hampir semua pekerjaan dilakukan di dalam terminal, menggunakan Git, kubectl, tmux, koneksi ssh ke server, dan lain-lain sepanjang hari
- Karena begitu sering dipakai, alat ini harus cepat; jeda saat membuka tab baru, mengetik karakter, atau tab completion terasa ratusan kali setiap hari
- Akumulasi jeda kecil seperti ini ibarat death by a thousand cuts
Hasil pengukuran kecepatan startup shell
- Setelah berbagai pembaruan, shell kini mulai dalam sekitar 30 milidetik, diukur dengan perintah
for i in {1..5}; do /usr/bin/time zsh -i -c exit; done - Shell interaktif lengkap dengan autocompletion, syntax highlighting, autosuggestion, fzf, dan direnv semuanya termuat dalam waktu yang lebih singkat dari satu frame tunggal pada 30fps
- Ini bukan hasil dari satu proyek optimasi besar, melainkan buah dari kebiasaan bertahun-tahun menjaga shell tetap minimal dan cepat
- Semua konfigurasinya dipublikasikan di repositori dotfiles
Tanpa framework
- Keuntungan terbesar justru datang dari hal yang tidak ada: tidak memakai oh-my-zsh, prezto, maupun plugin manager
- Jika hanya memakai sekitar 5% dari ratusan plugin dan tema milik oh-my-zsh, maka setiap kali membuka shell kita tetap membayar biaya waktu dan sumber daya komputasi untuk 95% sisanya
- Plugin manager menambah overhead lagi di atas itu
- Yang dipakai tepat 3 plugin saja, dan skrip instalasi melakukan
git clonesatu kali lalusourcedari.zshrcfzf-tab,zsh-autosuggestions,zsh-syntax-highlighting- Tidak ada plugin manager yang melakukan resolusi dependensi saat startup, dan
sourcefile yang sudah ada di disk pada praktiknya hampir tidak berbiaya
Caching autocompletion
compinitadalah salah satu pekerjaan termahal dalam.zshrcbiasa, karena secara default ia melakukan audit keamanan terhadap semua file autocompletion setiap kali shell dibuka- Solusinya adalah menjalankan proses penuh hanya jika cache (
.zcompdump) lebih tua dari 24 jam, dan selain itu melewati pemeriksaan dengan-C- Penentu glob
#qNmh-24berarti "ada dan dimodifikasi dalam 24 jam terakhir" - Jadi
compinitpenuh hanya dijalankan sekali sehari, dan selebihnya menggunakan pembacaan cache
- Penentu glob
Lazy-loading
- nvm adalah salah satu penyebab startup shell lambat yang paling terkenal; jika langsung di-
sourcesaat startup, ia bisa dengan mudah menambah 0,5 detik - nvm tidak dibutuhkan di semua shell, melainkan hanya saat mengetik
nvm, jadi ia dibungkus dalam fungsi yang menggantikan dirinya sendiri saat pertama kali dipakai- Panggilan
nvmpertama menghapus stub, me-sourcenvm yang sebenarnya (dengan--no-useagar tidak sekaligus menyelesaikan versi node), lalu meneruskan argumennya
- Panggilan
- Autocompletion kubectl juga memakai pendekatan yang sama; karena ia memanggil biner
kubectluntuk menghasilkan skrip completion, maka ia hanya dimuat setelah benar-benar dijalankan pertama kali - Semua tool yang menyuruh menaruh
eval "$(tool init zsh)"di.zshrcakan melakukan fork proses saat startup lalu mengevaluasi outputnya, jadi itu adalah kandidat lazy-loading - direnv dan fzf cepat dan sering dipakai, jadi tetap dimuat langsung; yang penting adalah menilai dengan ketat apa yang benar-benar sering digunakan
Prompt non-blocking
- Prompt yang menjalankan
git statussecara sinkron akan melambat di repositori yang agak besar, dan ini terasa setiap kali menekan Enter sehingga bisa lebih buruk daripada startup yang lambat - Digunakan pure, yang merender prompt segera lalu mengisi informasi git secara asinkron ketika sudah siap
- Pernah sempat mencoba menggantinya dengan
vcs_infobawaan zsh, tetapi perilaku asinkron pure terasa lebih baik - Git status asinkron juga bisa diimplementasikan sendiri di prompt, tetapi pure sudah membungkus kebutuhan itu dengan baik
Emulator terminal itu sendiri
- Startup shell hanyalah setengah dari cerita; emulator itu sendiri juga menambah latensi input
- Digunakan Ghostty, terminal native berakselerasi GPU, dengan konfigurasi hanya 7 baris
- Dikombinasikan dengan alias
tmux new -A -s main(t), jendela terminal baru langsung mengembalikan ke sesi yang sudah ada
Cara mengukur performa shell sendiri
- Kita bisa langsung mengukur ke mana waktu di terminal terpakai, dan ada tiga jenis jeda yang perlu dicek: waktu startup, latensi prompt, dan latensi input
- Pengukuran dasar dilakukan dengan menjalankan
time zsh -i -c exitbeberapa kali; eksekusi pertama selalu lebih lambat karena cold cache- Di bawah 100ms sudah oke, di bawah 50ms luar biasa, dan di atas 500ms berarti ada yang perlu dibenahi
- Untuk statistik yang akurat, gunakan hyperfine:
hyperfine --warmup 3 'zsh -i -c exit' - Manfaatkan profiler bawaan zsh
- Tambahkan
zmodload zsh/zprofdi bagian paling atas.zshrcdanzprofdi bagian paling bawah untuk menampilkan tabel terurut tentang ke mana waktu habis digunakan - Item teratas biasanya
compinit,sourceuntuknvm.sh, daneval "$(...)"; perbaiki mulai dari yang paling atas lalu ulangi pengujian - Setelah selesai, hapus dua baris itu
- Tambahkan
- Jika zprof belum cukup, lacak seluruh startup dengan timestamp:
zsh -ixc exit 2>&1 | ts -i '%.s' | sort -rn | head -20- Atau set
PS4='+%D{%s.%6.}: 'lalu jalankanzsh -ixc exit 2> startup.log, kemudian cari loncatan besar antarbaris
- Atau set
- Startup bisa cepat tetapi redraw prompt tetap lambat; pindah ke repositori git terbesar dengan
cd, lalu tekan Enter. Jika ada jeda sebelum prompt berikutnya muncul, berarti prompt melakukan pekerjaan sinkron- Opsinya adalah beralih ke prompt asinkron atau menghapus fitur Git
Penutup
- Sebagian besar optimasi adalah soal membuang hal-hal yang tidak perlu, dan kuncinya adalah bertindak dengan sengaja serta hanya menambahkan apa yang benar-benar akan dipakai
- Dengan begitu, puluhan sesi yang dibuka setiap hari semuanya terbuka seketika, dan terminal terasa bukan sebagai aplikasi yang harus ditunggu, melainkan alat yang seperti perpanjangan pikiran
- Untuk alat yang dipakai sepanjang hari, kecepatan seperti ini tidak bisa ditawar
- Semua konfigurasi di atas dipublikasikan di repositori dotfiles
1 komentar
Komentar Lobste.rs
Secara teknis, yang dimaksud kebanyakan bukan terminal, melainkan shell
Lebih baik memakai alat dengan default yang benar, jadi pakai saja fish
Saya suka karena fitur seperti tab completion modern yang bisa dipilih dengan tombol panah sudah tersedia secara bawaan, dan di perangkat pribadi saya masih memakai ZSH, tapi itu karena saya belum sempat mengutak-atik pengaturan Nix dan home manager
Shell dengan default yang masuk akal dan completion bawaan yang cepat, tanpa harus membuang atau menulis ulang alat-alat berbasis bash
Kadang saya bertanya-tanya apakah hal-hal seperti prompt non-blocking atau terminal berbasis OpenGL benar-benar sepadan dibanding sekadar memakai
PS1="\W: "di xtermDitambah lagi sangat cepat dan punya keunggulan sebagai “standar”, jadi bug yang tersisa pun umumnya kecil atau program yang berjalan di dalamnya kemungkinan akan menganggap perilakunya normal
Jadi saya kembali memakai xterm
Startup zsh pada dasarnya sangat cepat, dan hanya melambat kalau pengguna sendiri yang membuatnya lambat
Cukup jangan memasukkan banyak hal yang tidak dipahami, termasuk library yang menyebut dirinya “minimal” tetapi mengeksekusi ratusan perintah setiap kali membangun prompt
Konfigurasi zsh saya adalah beberapa ratus baris yang berkembang sangat pelan sejak era 90-an, dan saya memahami setiap barisnya serta tahu kenapa itu ada
Saya tidak pernah secara khusus mencoba membuatnya cepat, tapi tetap mulai dalam 20ms, dan kalau saya membuat perubahan bodoh yang bisa memperlambatnya, saya langsung sadar dan bisa memperbaikinya
Saya tidak suka benchmark rusak seperti
time zsh -i -c exityang masih sering dipakaiItu mengukur hal yang sepenuhnya salah, dan beberapa pengelola plugin zsh sampai mengorbankan latensi startup shell yang nyata demi mengoptimalkan metrik tak berguna ini
zsh-bench punya bagian yang menjelaskan kenapa benchmark ini tidak bermakna: https://github.com/romkatv/zsh-bench#how-not-to-benchmark
Metrik seperti first prompt latency atau input latency yang diukur zsh-bench jauh lebih berguna
Saya senang ternyata ini bukan soal bug terminal dengan akselerasi GPU
Caching completion adalah tip yang bagus, dan saya memakai zsh di Mac kantor, sampai-sampai baru terpikir membuka tab baru saja beachball sudah muncul, jadi semoga ini membantu
Untuk completion kubectl, saya penasaran bagian lambatnya ada pada pembuatan completion atau saat memuatnya, dan kalau yang lambat adalah yang pertama, apakah menyimpannya ke file lalu memuat dari sana akan mengurangi waktu startup
Di
jjmereka melakukan itu, dan sejak pindah kejjsaya juga membuang prompt yang menjalankangit statusSayang penulis tidak menampilkan waktunya sendiri, jadi saya tidak bisa tahu apakah 0,287 detik saya itu rata-rata atau lambat
Setelah saya ukur,
.bashrcyang hampir kosong memakan 0,007 detik, setelah key binding skim jadi 0,043 detik, setelah mise jadi 0,115 detik, setelah completion jj jadi 0,186 detik, dan jika sampai membaca/etc/bashrcjadi 0,294 detik, jadi tampaknya masih ada ruang untuk perbaikantime shell -c exityang sama, milik saya sekitar 50msHal paling menjengkelkan saat memakai lingkungan Linux orang lain adalah animasi tidak berguna yang ada di mana-mana
Di komputer saya, saat menekan shortcut, jendela terminal terbuka nyaris seketika, kadang hanya terlihat kedipan singkat antara jendela dan prompt
Karena itu yang penting adalah pengujian end-to-end penuh: buka jendela baru, lakukan sesuatu di shell, lalu tutup, dan
time mytermlalu menekan Ctrl+D di jendela untuk menutupnya selalu di bawah 0,120 detikJika animasi dan compositing yang tidak perlu dihilangkan, banyak hal jadi mungkin, dan saat melihat perbedaan dua spreadsheet saya juga memaksimalkan dua jendela lalu berganti cepat dengan shortcut roll-up jendela, sehingga perbedaannya langsung terlihat
Melakukan hal yang sama di Windows dengan animasi Excel terlalu mengganggu
Bahkan dengan konfigurasi kosong,
zsh -i -c exitrata-ratanya 129,8ms, dan konfigurasi penuh memakan sekitar 250ms, jadi kurang lebih samaDengan caching compinit saya memang memangkas rata-rata sekitar 5ms, tapi karena completion bisa saja hilang, menurut saya usahanya tidak terlalu sepadan
Belakangan startup zsh terasa melambat hampir seperti macet total, dan meskipun saya belum menemukan penyebab pastinya, saya sudah memastikan bahwa compinit mengambil sebagian besar jalur kritis
Saya mengimplementasikan caching hampir sama seperti yang disarankan artikel itu dan berhasil menghilangkan kelambatan tersebut, dan setelah melihat glob qualifier yang keren itu saya merasa cara saya juga harus diperbaiki
Saya bahkan tidak tahu fitur seperti itu memungkinkan, dan sejujurnya terlihat agak mencurigakan, tapi tetap akan saya pakai
Sebelumnya saya memakai cara yang agak kasar dengan
date -Idsaat membuat path targetSaya suka alat yang dikonfigurasi dengan bahasa pemrograman yang benar-benar lengkap seperti zsh, sehingga kita bisa mengimplementasikan sendiri tanpa perlu penulisnya menambahkan fitur caching
Selama hampir 20 tahun memakai zsh saya tidak pernah memakai framework atau plugin manager, dan tampaknya hal-hal seperti itu lebih sering dipakai untuk styling
Saya beruntung karena tidak peduli dengan estetika lingkungan komputasi saya, dan prompt buatan saya sendiri juga sederhana, kecil, informatif, tapi sama sekali tidak mewah, serta saya memakai tema terminal default berlatar hitam
Beberapa instance shell bisa melakukan hal yang sama secara paralel, dan saya sering mengalaminya saat menjalankan instance paralel untuk latihan di tmux
Selain itu, home directory juga bisa dibagi di antara beberapa host, terutama container, jadi akhirnya saya menatanya dengan pendekatan yang mencakup lock file, pemeriksaan kedaluwarsa, dan penanganan kondisi
zcompileSayangnya, konfigurasi fish juga tampaknya perlahan bergerak ke arah yang sama, dan saat waktu senggang hari Senin saya berencana melakukan profiling untuk melihat apakah teknik lazy loading benar-benar berguna untuk kasus saya
Sebagian besar waktu lambatnya kemungkinan berasal dari modul git milik Starship, tapi ada juga cukup banyak alias dan fungsi helper yang bisa di-lazy-load
Di Emacs, sejak lama sudah ada shell staging di background yang diinisialisasi lebih dulu
Membuka terminal berarti membuka jendela baru ke buffer itu lalu mengganti namanya, dan mem-fork thread yang menyiapkan shell lagi untuk penggunaan berikutnya
Jadi tidak ada latensi startup
Saya ingat dulu pernah mencoba memaksa solusi di luar Emacs dengan reptyr, tapi akhirnya tidak terus memakainya, dan saya juga tidak terlalu ingat alasannya
https://github.com/nelhage/reptyr
Saat menyelidiki hal serupa, saya menemukan bahwa
zsh-abbrmemakan sekitar 100ms waktu startup, tapi saya masih oke dengan ituSaya mungkin bisa memangkas 10ms di sana-sini, tapi kalau melihat fitur yang hilang, rasanya tidak sepadan
Saya akan hidup dengan waktu startup sekitar 300ms; itu cukup cepat, dan saya juga jarang membuka terminal bertubi-tubi atau harus langsung mengetik saat itu juga
Meski begitu, artikelnya bagus, saya jadi tahu
hyperfine, dan jadi melihat-lihat beberapa file startup zshBerkat ini saya akhirnya memperbaiki zshrc yang sudah lama tertunda, dan sekarang turun sampai 80ms, sangat mantap
Hidup saya cukup panjang untuk menoleransi terminal yang lambat, dan kadang saya malah berharap terminal lebih lambat
Misalnya, kalau konsol root punya jeda default 5 detik sebelum benar-benar mengeksekusi, agar ada waktu membatalkan typo dengan Ctrl+C, mungkin saya bisa menghemat beberapa hari di masa muda saya yang dulu suka nekat