Yavaş bir terminalde yazmak için hayat çok kısa
(mijndertstuij.nl)- Her gün boyunca kullanılan terminalin hızı, çalışma verimliliğini belirler; yeni sekme açma, yazı yazma ve otomatik tamamlama sırasındaki küçük gecikmeler günde yüzlerce kez birikince verimsiz hale gelir
- Tam yüklenmiş etkileşimli kabuk, otomatik tamamlama, sözdizimi vurgulama, otomatik öneriler, fzf ve direnv dahil olduğu halde yaklaşık 30 milisaniyede başlıyor ve yeni sekmeler anında açılacak şekilde iyileştirildi
- En büyük sır, oh-my-zsh ya da prezto gibi framework ve eklenti yöneticileri kullanmamak; sadece 3 eklenti doğrudan
git cloneile alınıp.zshrciçindesourceediliyor compinitönbellekleme, tembel yükleme (lazy-loading), asenkron prompt, GPU hızlandırmalı terminal gibi yöntemlerle başlangıç, prompt ve giriş gecikmesi en aza indiriliyor- Optimizasyonların çoğu bir şey eklemekten çok gereksiz olanları çıkarmakla ilgili; gerçekten sık kullanılanları bilinçli biçimde ekleme disiplini asıl mesele
Neden hızlı bir terminal gerekli
- Neredeyse tüm işler terminal içinde yapılıyor; Git, kubectl, tmux, sunuculara ssh bağlantısı gibi araçlar gün boyu kullanılıyor
- Bu kadar sık kullanılan bir aracın hızlı olması gerekir; yeni sekme açarken, karakter girerken ve Tab ile otomatik tamamlama yaparken oluşan gecikme günde yüzlerce kez hissedilir
- Bu küçük gecikmelerin birikmesi, bin kesikle ölüm (death by a thousand cuts) gibidir
Kabuk başlangıç hızı ölçüm sonuçları
- Güncelleme sonucunda kabuk yaklaşık 30 milisaniyede başlıyor; ölçüm için
for i in {1..5}; do /usr/bin/time zsh -i -c exit; donekomutu kullanılmış - Otomatik tamamlama, sözdizimi vurgulama, otomatik öneriler, fzf ve direnv içeren tam etkileşimli bir kabuk, 30fps tek kareden daha kısa sürede yükleniyor
- Ortada büyük bir optimizasyon projesi yok; bu, yıllar boyunca kabuğu sade ve hızlı tutma alışkanlığının sonucu
- Tüm ayarlar dotfiles deposunda açık olarak paylaşılıyor
Framework yok
- En büyük kazanç, var olmayan şeylerden geliyor; oh-my-zsh, prezto ve eklenti yöneticileri kullanılmıyor
- oh-my-zsh içindeki yüzlerce eklenti ve temanın yalnızca yaklaşık %5’i kullanılırken, kalan %95 için gereken zaman ve hesaplama kaynağı maliyeti her kabuk açılışında ödeniyor
- Eklenti yöneticileri bunun üstüne ek yük daha bindiriyor
- Yalnızca tam 3 eklenti kullanılıyor ve kurulum betiği bunları bir kez
git cloneile alıp ardından.zshrciçindesourceediyorfzf-tab,zsh-autosuggestions,zsh-syntax-highlighting- Başlangıçta bağımlılık çözümü yapan bir eklenti yöneticisi yok; diskte zaten bulunan dosyaları
sourceetmek ise pratikte neredeyse bedava
Otomatik tamamlama önbellekleme
compinit, tipik bir.zshrciçindeki en maliyetli işlemlerden biri; varsayılan olarak her kabuk açılışında tüm otomatik tamamlama dosyaları için güvenlik denetimi yapıyor- Çözüm, önbellek (
.zcompdump) 24 saatten daha eskiyse yalnızca o zaman tam çalıştırma yapmak, aksi halde-Cile denetimi atlamak- glob niteleyicisi
#qNmh-24, "var ve son 24 saat içinde değiştirilmiş" anlamına geliyor - Böylece tam
compinitgünde yalnızca bir kez çalışıyor, geri kalan zamanda önbellekli okuma kullanılıyor
- glob niteleyicisi
Tembel yükleme (Lazy-loading)
- nvm, kabuk başlangıç hızını düşüren en kötü şöhretli nedenlerden biri; başlangıçta hemen
sourceedilirse kolayca 0,5 saniye ekleyebilir - Her kabukta nvm gerekli değil; yalnızca
nvmyazıldığında gerekiyor, bu yüzden ilk kullanımda kendisini gerçek yükleyiciyle değiştiren bir fonksiyonla sarılıyor- İlk
nvmçağrısı geçici sarmalayıcıyı silip gerçek nvm’yisourceediyor (--no-useile node sürümü çözümlemesi de engelleniyor) ve sonra argümanları iletiyor
- İlk
- kubectl otomatik tamamlama da aynı yöntemle ele alınıyor; otomatik tamamlama betiğini oluşturmak için
kubectlikilisini çağırdığı için ancak gerçekten ilk kullanımdan sonra yükleniyor .zshrciçineeval "$(tool init zsh)"eklemenizi söyleyen her araç, başlangıçta süreç fork edip çıktıyı değerlendirdiğinden tembel yükleme adayı- direnv ve fzf hızlı ve sık kullanıldıkları için anında yüklenen durumda bırakılıyor; gerçekten neyin sık kullanıldığını katı biçimde değerlendirmek gerekiyor
Bloklamayan prompt
git statuskomutunu eşzamanlı çalıştıran bir prompt, bir miktar büyük depolarda gecikme yaratır; bu da her Enter tuşuna bastığınızda hissedildiği için yavaş başlangıçtan bile daha kötü olabilir- pure kullanılıyor; prompt hemen render ediliyor, Git bilgisi ise hazır olduğunda asenkron biçimde dolduruluyor
- Bir süre zsh yerleşik
vcs_infoile değiştirme denemesi yapılmış ama pure’un asenkron davranışı daha iyi bulunmuş - İstenirse asenkron
git statusdoğrudan prompt içine de uygulanabilir, ancak pure bunu bu kullanım için oldukça iyi soyutluyor
Terminal emülatörünün kendisi
- Kabuk başlangıcı hikâyenin yalnızca yarısı; emülatörün kendisi de giriş gecikmesi ekler
- GPU hızlandırmalı yerel bir terminal olan Ghostty kullanılıyor ve ayarlar sadece 7 satır
tmux new -A -s maintakma adı (t) ile birlikte kullanıldığında yeni terminal penceresi doğrudan mevcut oturuma geri döndürüyor
Kendi kabuk performansınızı nasıl ölçersiniz
- Sürenin nereye harcandığını doğrudan terminalde ölçebilirsiniz; bakılması gereken üç gecikme türü başlangıç süresi, prompt gecikmesi ve giriş gecikmesi
- Temel ölçüm için
time zsh -i -c exitbirkaç kez çalıştırılır; ilk çalıştırma soğuk önbellek nedeniyle her zaman daha yavaştır- 100 ms altı kabul edilebilir, 50 ms altı mükemmel, 500 ms üstü ise müdahale gerektirir
- Daha doğru istatistikler için hyperfine kullanın:
hyperfine --warmup 3 'zsh -i -c exit' - zsh içine gömülü profil aracını kullanın
.zshrcdosyasının en üstünezmodload zsh/zprof, en altınazprofeklerseniz sürenin nereye gittiğini sıralı tablo halinde gösterir- En üst kalemler genelde
compinit,nvm.shdosyasınısourceetme veeval "$(...)"olur; en üsttekinden başlayıp düzeltme yaparak tekrar çalıştırın - İşiniz bitince bu iki satırı kaldırın
- zprof yetmezse tüm başlangıcı zaman damgalarıyla izleyin:
zsh -ixc exit 2>&1 | ts -i '%.s' | sort -rn | head -20- Ya da
PS4='+%D{%s.%6.}: 'ayarlayıpzsh -ixc exit 2> startup.logçalıştırın, sonra satırlar arasındaki büyük sıçramalara bakın
- Ya da
- Başlangıç hızlı olsa bile prompt yeniden çizimi yavaş olabilir; en büyük Git deponuza
cdyaptıktan sonra Enter’a basın, bir sonraki prompt görünmeden önce gecikme varsa prompt eşzamanlı iş yapıyordur- Asenkron prompt’a geçebilir veya Git özelliklerini kaldırabilirsiniz
Sonuç
- Optimizasyonların çoğu bir şeyleri çıkarmakla ilgilidir; bilinçli davranmak ve yalnızca gerçekten kullanacaklarınızı eklemek temel noktadır
- Böyle yapıldığında gün içinde açılan onlarca oturumun hepsi anında açılır ve terminal, beklenmesi gereken bir uygulama değil zihnin bir uzantısı gibi hissettiren bir araç haline gelir
- Gün boyu kullanılan bir araç için bu hız taviz verilemez
- Yukarıdaki tüm ayarlar dotfiles deposunda açık olarak paylaşılmış durumda
1 yorum
Lobste.rs görüşleri
Teknik olarak çoğu durumda kastedilen şey terminal değil, shell
Varsayılanları düzgün olan araçları kullanmak daha iyi; bu yüzden fish kullanmak yeterli
Ok tuşlarıyla seçilebilen modern sekme tamamlama gibi özelliklerin varsayılan gelmesi güzeldi; kişisel cihazlarımda hâlâ ZSH kullanıyorum ama bunun tek nedeni Nix ayarlarımı ve home manager yapılandırmamı elden geçirecek zaman bulamamış olmam
Mantıklı varsayılanları ve hızlı yerleşik tamamlaması olan, ama bash tabanlı araçları terk etmeyi ya da yeniden yazmayı gerektirmeyen bir shell güzel olurdu
Bazen engellemeyen prompt ya da OpenGL tabanlı terminaller gibi şeylerin, xterm’de sadece
PS1="\W: "kullanmaya kıyasla gerçekten buna değip değmediğini merak ediyorumÜstelik çok hızlı ve “standart” olma avantajına sahip; bu yüzden kalan hatalar da genelde önemsiz oluyor ya da içinde çalışan programların normal davranış kabul etme ihtimali yüksek oluyor
Bu yüzden tekrar xterm kullanmaya başladım
zsh başlangıcı aslında çok hızlıdır; yavaşlaması ancak kullanıcı onu yavaşlattığında olur
Anlamadığınız bir sürü şeyi içine doldurmamak yeterli; buna, kendine “minimal” deyip her prompt oluşturulduğunda yüzlerce komut çalıştıran kütüphaneler de dahil
Benim zsh yapılandırmam 90’lardan beri çok yavaş şekilde evrilen birkaç yüz satırlık bir dosya; her satırını anlıyorum ve neden orada olduğunu biliyorum
Özellikle hızlı olsun diye hiç uğraşmadım ama yine de 20ms’de başlıyor; yavaşlatabilecek aptalca bir değişiklik yaparsam da hemen fark edip düzeltebiliyorum
time zsh -i -c exitgibi bozuk benchmarkların hâlâ yaygın kullanılmasından hoşlanmıyorumTamamen yanlış şeyi ölçüyorlar ve bazı zsh eklenti yöneticileri, gerçek shell başlangıç gecikmesini feda etme pahasına bu işe yaramaz metriğe göre optimize edildi
zsh-bench içinde bu benchmark’ın neden anlamsız olduğunu açıklayan bir bölüm var: https://github.com/romkatv/zsh-bench#how-not-to-benchmark
zsh-bench’in ölçtüğü ilk prompt gecikmesi ya da giriş gecikmesi gibi metrikler çok daha faydalı
Bunun GPU hızlandırmalı terminal hatalarıyla ilgili olacağını sanmıştım; öyle çıkmamasına sevindim
Tamamlama önbellekleme iyi bir ipucu ve iş için kullandığım Mac’te zsh çalıştırıyorum; yeni sekme açmayı düşününce bile beachball çıktığı için umarım işe yarar
kubectl tamamlama için yavaş olan kısmın tamamlama üretimi mi yoksa yüklenmesi mi olduğunu merak ediyorum; eğer ilkiyse dosyaya kaydedip sonra okumak başlangıç süresini azaltır mı diye de merak ediyorum
jjiçin bunu yapıyorum vejjye geçince prompt’tangit statusçalıştırmayı da çıkardımKeşke yazar kendi sürelerini de gösterseydi; o zaman benim 0.287 saniyemin ortalama mı yoksa yavaş mı olduğunu anlayabilirdim
Sonra ölçtüm: neredeyse boş
.bashrc0.007 saniye, skim tuş bağları sonrası 0.043 saniye, mise sonrası 0.115 saniye, jj tamamlama sonrası 0.186 saniye,/etc/bashrcde okununca 0.294 saniye; yani iyileştirme alanı var gibi görünüyortime shell -c exittestinde benimki yaklaşık 50ms çıkıyorBaşka insanların Linux ortamlarını kullanırken beni en çok sinirlendiren şey her yerdeki gereksiz animasyonlar
Kendi bilgisayarımda kısayola bastığım anda terminal penceresi neredeyse anında açılıyor; bazen pencere ile prompt arasında sadece kısa bir yanıp sönme görüyorum
Bu yüzden yeni pencere açıp shell içinde bir şey yapıp kapatmayı içeren uçtan uca test önemli;
time mytermdeyip pencerede Ctrl+D ile kapattığımda sonuç hep 0.120 saniyenin altındaydıGereksiz animasyon ve compositing’i kaldırınca yapılabilecek çok şey oluyor; iki elektronik tablo arasındaki farklara bakarken de iki pencereyi büyütüp pencere sarma kısayoluyla hızla aralarında geçince farklar anında görünüyordu
Windows’ta aynı şeyi Excel animasyonları eşliğinde yapmak fazla dikkat dağıtıcı
Boş ayarlarla bile
zsh -i -c exitortalaması 129.8ms ve tam yapılandırma da benzer şekilde yaklaşık 250ms sürüyorcompinit önbelleklemesiyle ortalamada yaklaşık 5ms kazandım ama tamamlamalar eksik kalabildiği için verilen emeğe pek değdiğini düşünmüyorum
Son zamanlarda zsh başlangıcı neredeyse donmuş gibi yavaşlamıştı; kesin sebebi bulamadım ama kritik yolun büyük kısmını compinit’in kapladığını doğruladım
Yazıda önerilen yönteme çok benzer şekilde önbellekleme uygulayarak yavaşlamayı giderdim; ayrıca o havalı glob qualifier’ı görünce kendi yöntemimi de iyileştirmem gerektiğini düşündüm
Böyle bir şeyin mümkün olduğunu bilmiyordum; açıkçası biraz şüpheli bir özellik gibi görünüyor ama yine de kullanacağım
Daha önce hedef yolu oluştururken nispeten kaba bir
date -Idyaklaşımı kullanıyordumzsh gibi tam teşekküllü bir programlama diliyle yapılandırılan araçları seviyorum; böylece yazarın önbellekleme özelliği eklemesini beklemeden bunu kendiniz uygulayabiliyorsunuz
Yaklaşık 20 yıldır zsh kullanıyorum; hiç framework ya da eklenti yöneticisi kullanmadım ve bunlar daha çok stil amaçlı kullanılıyor gibi görünüyor
Ben bilişim ortamımın estetiğine önem vermediğim için şanslıyım; kendi yazdığım prompt da basit, küçük ve bilgilendirici ama hiç gösterişli değil ve siyah arka planlı varsayılan terminal temasını kullanıyorum
Birden fazla shell örneği paralel olarak aynı işi yapabiliyor; tmux içinde deneme amaçlı paralel örnekler açarken bunu sık yaşadım
Ayrıca birden fazla host, özellikle de container’lar arasında home dizinini paylaşıyor olabilirsiniz; bu yüzden sonunda kilit dosyaları, son kullanma kontrolü ve
zcompilekoşul işlemesini de içeren bir yöntemle toparladımNe yazık ki fish yapılandırması da yavaş yavaş aynı yöne kaymış gibi görünüyor; pazartesi günü boş vaktimde profilleme yapıp lazy loading tekniklerinin gerçekten benim durumda işe yarayıp yaramadığını göreceğim
Yavaşlamanın büyük kısmı muhtemelen Starship’in git modülünden geliyor ama lazy load edilebilecek epey alias ve yardımcı fonksiyon da var
Emacs’te uzun zamandır arka planda hazır bekleyen shell önceden başlatılıyor
Terminal açmak demek o buffer için yeni bir pencere açıp yeniden ad vermek; ardından bir sonraki kullanım için shell’i tekrar hazırlayan bir thread fork ediliyor
Bu yüzden başlangıç gecikmesi yok
Eskiden reptyr ile Emacs dışı bir çözümü de zorlamaya çalıştığımı hatırlıyorum ama sonunda onu kullanmaya devam etmedim; nedenini de pek hatırlamıyorum
https://github.com/nelhage/reptyr
Benzer şekilde kurcalarken
zsh-abbrın başlangıç süresinden yaklaşık 100ms yediğini fark ettim ama o kadar sorun değilOradan buradan 10ms azaltılabilir ama kaybedilen işlevi düşününce buna değmiyor gibi geliyor
Yaklaşık 300ms başlangıç süresiyle yaşamaya razıyım; yeterince hızlı ve zaten ardı ardına terminal açıp anında yazı yazmam gereken durumlar pek olmuyor
Yine de yazı güzeldi;
hyperfineı öğrenmiş oldum ve birkaç zsh başlangıç dosyasına dönüp baktımSayesinde uzun süredir ertelediğim zshrc düzenlemesini yaptım; şimdi 80ms’ye düştü ve harika oldu
Benim hayatım yavaş terminalleri kaldıracak kadar uzun ve bazen terminalin daha da yavaş olmasını diliyorum
Mesela root konsolunda gerçekten çalıştırmadan önce varsayılan 5 saniyelik gecikme olsaydı ve bu sayede yazım hatasını Ctrl+C ile iptal etme fırsatı verseydi, asi gençlik yıllarımda birkaç gün kurtarabilirdim