5 पॉइंट द्वारा GN⁺ 4 시간 전 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • पूरे दिन इस्तेमाल होने वाले टर्मिनल की speed काम की efficiency तय करती है, और नया tab खोलने, typing, और autocomplete में होने वाली छोटी-छोटी delays अगर दिन में सैकड़ों बार जुड़ें, तो वे अक्षम बन जाती हैं
  • पूरी तरह लोड हुआ interactive shell, जिसमें autocomplete, syntax highlighting, autosuggestions, fzf, और direnv शामिल हैं, लगभग 30 मिलीसेकंड में शुरू हो जाता है, और नया tab लगभग तुरंत खुल जाता है
  • सबसे बड़ा रहस्य यह है कि oh-my-zsh या prezto जैसे frameworks और plugin managers का उपयोग नहीं किया जाता, बल्कि सिर्फ 3 plugins को सीधे git clone करके .zshrc में source किया जाता है
  • compinit caching, lazy-loading, asynchronous prompt, और GPU-accelerated terminal जैसी तकनीकों से startup, prompt, और input latency सभी को न्यूनतम किया गया है
  • ज़्यादातर optimization कुछ जोड़ने के बारे में नहीं, बल्कि अनावश्यक चीज़ें हटाने के बारे में है; असली कुंजी है कि जो वास्तव में अक्सर इस्तेमाल होता है, वही सोच-समझकर जोड़ा जाए

तेज़ टर्मिनल की ज़रूरत क्यों है

  • लगभग हर काम टर्मिनल के भीतर होता है, और Git, kubectl, tmux, server पर ssh login जैसी चीज़ें पूरे दिन इस्तेमाल होती हैं
  • जो टूल इतना ज़्यादा इस्तेमाल होता है, वह तेज़ होना चाहिए; नया tab खोलने, characters टाइप करने, और tab autocomplete में होने वाली delays दिन में सैकड़ों बार महसूस होती हैं
  • इस तरह की छोटी-छोटी delays का जमा होना death by a thousand cuts जैसा है

shell startup speed के माप का परिणाम

  • अपडेट के बाद shell लगभग 30 मिलीसेकंड में शुरू होता है, और मापने के लिए for i in {1..5}; do /usr/bin/time zsh -i -c exit; done कमांड इस्तेमाल की गई
  • autocomplete, syntax highlighting, autosuggestions, fzf, और direnv के साथ पूरा interactive shell 30fps के एक single frame से भी कम समय में लोड हो जाता है
  • यह किसी एक बड़े optimization project का नतीजा नहीं, बल्कि वर्षों तक shell को minimal और तेज़ बनाए रखने की आदत का परिणाम है
  • सभी settings dotfiles repository में सार्वजनिक हैं

कोई framework नहीं

  • सबसे बड़ा फायदा उस चीज़ से आता है जो मौजूद ही नहीं है: oh-my-zsh, prezto, और plugin manager का उपयोग नहीं किया जाता
  • oh-my-zsh के सैकड़ों plugins और themes में से लगभग 5% ही इस्तेमाल होते हैं, लेकिन shell खोलते समय बाकी 95% के लिए भी समय और computing resources की कीमत चुकानी पड़ती है
  • plugin managers इसके ऊपर अतिरिक्त overhead जोड़ते हैं
  • सिर्फ 3 plugins इस्तेमाल होते हैं, जिन्हें install script एक बार git clone करती है और फिर .zshrc से source किया जाता है
    • fzf-tab, zsh-autosuggestions, zsh-syntax-highlighting
    • startup के समय dependencies resolve करने वाला कोई plugin manager नहीं है; disk पर पहले से मौजूद files को source करना लगभग बिना लागत का काम है

autocomplete caching

  • compinit सामान्य .zshrc में सबसे महंगे कामों में से एक है, क्योंकि यह default रूप से हर बार shell खुलने पर सभी autocomplete files की security audit करता है
  • समाधान यह है कि पूरा run सिर्फ तब किया जाए जब cache (.zcompdump) 24 घंटे से पुरानी हो; बाकी समय -C के साथ check छोड़ दिया जाए
    • glob qualifier #qNmh-24 का अर्थ है "मौजूद है और पिछले 24 घंटों के भीतर modified हुई है"
    • पूरा compinit दिन में सिर्फ एक बार चलता है, और बाकी समय cached read का उपयोग होता है
    विज्ञापन

lazy-loading

  • nvm shell startup को धीमा करने वाले सबसे बदनाम कारणों में से एक है; startup पर तुरंत source करने से आसानी से 0.5 सेकंड जुड़ सकते हैं
  • हर shell में nvm की ज़रूरत नहीं होती; यह सिर्फ nvm टाइप करने पर चाहिए, इसलिए इसे एक ऐसे function में लपेटा जाता है जो पहली बार उपयोग होने पर खुद को replace कर देता है
    • पहला nvm call stub को हटाता है, असली nvm को source करता है (--no-use के साथ ताकि node version resolution भी न हो), और फिर arguments आगे भेज देता है
  • kubectl autocomplete भी इसी तरह काम करता है; यह kubectl binary को call करके autocomplete script बनाता है, इसलिए इसे भी पहली वास्तविक execution के बाद ही load किया जाता है
  • जो भी tools .zshrc में eval "$(tool init zsh)" डालने को कहते हैं, वे startup पर process fork करते हैं और output evaluate करते हैं, इसलिए वे lazy-loading के उम्मीदवार हैं
  • direnv और fzf तेज़ हैं और बार-बार उपयोग होते हैं, इसलिए उन्हें तुरंत load रहने दिया जाता है; असल में क्या अक्सर इस्तेमाल होता है, इस पर सख़्ती से निर्णय लेना चाहिए

non-blocking prompt

  • जो prompt synchronous तरीके से git status चलाता है, वह थोड़े बड़े repository में भी delay ला सकता है, और यह हर बार Enter दबाने पर महसूस होता है, इसलिए धीमे startup से भी बदतर हो सकता है
  • pure का उपयोग किया जाता है, जो prompt को तुरंत render करता है और git जानकारी तैयार होते ही asynchronously भर देता है
  • zsh built-in vcs_info से बदलने की थोड़ी कोशिश की गई थी, लेकिन pure का asynchronous behavior बेहतर निकला
  • चाहें तो prompt में asynchronous git status खुद implement किया जा सकता है, लेकिन pure इस उपयोग के लिए अच्छी abstraction देता है

terminal emulator खुद

  • shell startup कहानी का सिर्फ आधा हिस्सा है; emulator खुद भी input latency जोड़ता है
  • GPU-accelerated native terminal Ghostty इस्तेमाल किया जाता है, और इसकी configuration सिर्फ 7 lines की है
  • tmux new -A -s main alias (t) के साथ मिलाकर, नई terminal window तुरंत मौजूदा session में वापस ले जाती है
विज्ञापन

अपनी shell performance कैसे मापें

  • आप सीधे terminal में माप सकते हैं कि समय कहाँ जा रहा है; देखने लायक तीन delays हैं: startup time, prompt delay, और input latency
  • बुनियादी माप के लिए time zsh -i -c exit को कुछ बार चलाएँ; पहला run cold cache के कारण हमेशा धीमा होगा
    • 100ms से कम ठीक है, 50ms से कम शानदार है, और 500ms से ज़्यादा हो तो सुधार की ज़रूरत है
  • अधिक सटीक आँकड़ों के लिए hyperfine का उपयोग करें: hyperfine --warmup 3 'zsh -i -c exit'
  • zsh के built-in profiler का उपयोग
    • .zshrc की शुरुआत में zmodload zsh/zprof और अंत में zprof जोड़ें, तो समय कहाँ खर्च हो रहा है इसका sorted table मिलेगा
    • ऊपर आने वाली entries अक्सर compinit, nvm.sh source, और eval "$(...)" होती हैं; सबसे ऊपर वाली चीज़ से शुरू करके सुधारें और बार-बार दोबारा चलाएँ
    • काम पूरा होने पर ये दोनों lines हटा दें
  • अगर zprof पर्याप्त न हो, तो timestamps के साथ पूरे startup को trace करें: zsh -ixc exit 2>&1 | ts -i '%.s' | sort -rn | head -20
    • या PS4='+%D{%s.%6.}: ' सेट करें और zsh -ixc exit 2> startup.log चलाकर lines के बीच बड़े jumps देखें
  • startup तेज़ होने पर भी prompt redraw धीमा हो सकता है; अपनी सबसे बड़ी git repository में cd करें, फिर Enter दबाएँ, और अगर अगला prompt आने से पहले delay महसूस हो, तो prompt synchronous काम कर रहा है
    • ऐसे में asynchronous prompt पर जाएँ या Git features हटा दें

निष्कर्ष

  • ज़्यादातर optimization कुछ हटाने के बारे में है; जानबूझकर काम करना और सिर्फ वही जोड़ना जिसे आप वास्तव में इस्तेमाल करेंगे सबसे महत्वपूर्ण है
  • ऐसा करने पर दिन भर खुलने वाले दर्जनों sessions तुरंत खुलते हैं, और terminal किसी ऐसी application जैसा नहीं लगता जिसका इंतज़ार करना पड़े, बल्कि दिमाग़ के विस्तार जैसा tool लगता है
  • पूरे दिन इस्तेमाल होने वाले टूल के लिए यह speed गैर-समझौताकारी है
  • ऊपर की सभी settings dotfiles repository में सार्वजनिक हैं

1 टिप्पणियां

 
GN⁺ 4 시간 전
Lobste.rs की राय
  • सख्ती से कहें तो ज़्यादातर मामलों में मतलब terminal नहीं बल्कि shell होता है

  • बेहतर है कि ऐसे tools इस्तेमाल किए जाएँ जिनके default सही हों, इसलिए बस fish इस्तेमाल कर लो

    • हमारी कंपनी का ZSH करीब एक साल पहले से बेहूदगी की हद तक धीमा हो गया था, तो मैंने fish आज़माया और उसकी quality-of-life features मुझे सच में बहुत पसंद आईं
      यह अच्छा लगा कि modern tab completion जैसी चीज़ें, जिन्हें arrow keys से चुना जा सकता है, default में ही मिलती हैं; निजी मशीनों पर मैं अभी भी ZSH इस्तेमाल करता हूँ, लेकिन सिर्फ़ इसलिए कि Nix config और home manager को ठीक करने का समय नहीं मिला
    • अच्छा होगा अगर कोई bash-compatible fish बना दे
      ऐसा shell जिसमें समझदारी भरे defaults और तेज built-in completion हो, लेकिन bash-आधारित tools को छोड़ना या फिर से लिखना न पड़े
    • नया tool install करने के लिए ज़िंदगी बहुत छोटी है, बस sensible defaults मिल जाएँ तो काफ़ी है
  • कभी-कभी सोचता हूँ कि non-blocking prompt या OpenGL-आधारित terminal जैसी चीज़ें सच में उतनी क़ीमती हैं भी या नहीं, जब xterm में सिर्फ़ PS1="\W: " जैसा कुछ इस्तेमाल करने से काम चल जाता है

    • मैंने जानबूझकर कई सालों तक xterm इस्तेमाल नहीं किया, लेकिन अलग-अलग terminal emulators देखने के बाद हैरानी हुई कि xterm OpenType fonts, UTF-8, ज़्यादातर emoji, 24-bit color, और कम memory usage — सब support करता है
      ऊपर से यह बहुत तेज़ है और “standard” होने का फ़ायदा भी है, इसलिए जो bugs बचे हैं वे भी आम तौर पर मामूली होंगे या उसके अंदर चलने वाले programs उन्हें normal behavior मानकर चलते होंगे
      इसलिए मैं फिर से xterm पर लौट आया
    • उतना worth it नहीं है
      zsh startup मूल रूप से बहुत तेज़ होता है; यह तभी धीमा पड़ता है जब user खुद उसे धीमा बना दे
      बस ढेर सारी ऐसी चीज़ें मत जोड़ो जिन्हें तुम समझते नहीं; इसमें वे libraries भी शामिल हैं जो खुद को “minimal” कहती हैं लेकिन हर बार prompt बनाते समय सैकड़ों commands चला देती हैं
      मेरी zsh config कुछ सौ लाइनों की है, जो 90 के दशक से बहुत धीरे-धीरे विकसित हुई है, और मैं हर लाइन समझता हूँ और जानता हूँ कि वह क्यों है
      मैंने इसे खास तौर पर तेज़ बनाने की कोशिश कभी नहीं की, फिर भी यह 20ms में start हो जाती है, और अगर मैं कोई बेवकूफ़ी भरा बदलाव कर दूँ जिससे यह धीमी हो, तो मुझे तुरंत पता चल जाता है और मैं उसे ठीक कर सकता हूँ
  • यह खटकता है कि time zsh -i -c exit जैसे टूटे हुए benchmark आज भी आम तौर पर इस्तेमाल किए जाते हैं
    वे पूरी तरह ग़लत चीज़ को measure करते हैं, और कुछ zsh plugin managers ने तो वास्तविक shell startup latency की क़ीमत पर इस बेकार metric के लिए optimization भी की है
    zsh-bench में एक section है जो बताता है कि यह benchmark क्यों बेमानी है: https://github.com/romkatv/zsh-bench#how-not-to-benchmark
    zsh-bench जिन metrics को measure करता है, जैसे first prompt latency या input latency, वे कहीं ज़्यादा उपयोगी हैं

  • अच्छा लगा कि बात GPU-accelerated terminal bugs की नहीं निकली
    completion caching बढ़िया टिप है, और मैं कंपनी के Mac पर zsh इस्तेमाल करता हूँ जहाँ सिर्फ़ नया tab खोलने का सोचते ही beachball दिखने लगता है, तो उम्मीद है इससे मदद मिलेगी
    kubectl completion के मामले में जिज्ञासा है कि धीमापन completion generation में है या उसे load करने में; अगर पहला मामला है, तो क्या उसे file में save करके बाद में load करने से startup time कम होगा
    jj में मैं ऐसा ही करता हूँ, और jj पर जाने के साथ मैंने वह prompt भी छोड़ दिया जो git status चलाता था
    काश लेखक ने अपना timing भी दिखाया होता, तो पता चलता कि मेरे 0.287 सेकंड औसत हैं या धीमे
    बाद में नापने पर लगभग खाली .bashrc में 0.007 सेकंड, skim key binding के बाद 0.043 सेकंड, mise के बाद 0.115 सेकंड, jj completion के बाद 0.186 सेकंड, और /etc/bashrc पढ़ने पर 0.294 सेकंड आए, तो सुधार की गुंजाइश दिखती है

    • लेख में कहा गया था कि front-end पर shell खुद 30ms है, और उसी time shell -c exit टेस्ट में मेरा लगभग 50ms आता है
      दूसरे लोगों के Linux environments इस्तेमाल करते समय मुझे सबसे ज़्यादा चिढ़ हर जगह की बेकार animations से होती है
      मेरे कंप्यूटर पर shortcut key दबाते ही terminal window लगभग तुरंत खुल जाती है, कभी-कभी बस window और prompt के बीच एक हल्की सी blink दिखती है
      इसलिए end-to-end test ज़्यादा महत्वपूर्ण है, यानी नई window खोलना, shell में कुछ करना, और उसे बंद करना; time myterm के बाद window में Ctrl+D दबाकर बंद करने पर समय हमेशा 0.120 सेकंड से कम रहा
      बेकार animation और compositing हटाओ तो बहुत कुछ संभव हो जाता है; दो spreadsheets के बीच फ़र्क़ देखने के लिए मैं दो windows maximize करके window-rolling shortcut से तेज़ी से आगे-पीछे होता था और अंतर तुरंत दिख जाता था
      Windows में Excel animation के साथ वही काम करना बहुत distract करने वाला है
    • 100ms से कम मेरे environment में मुश्किल लगता है
      खाली config के साथ भी zsh -i -c exit औसतन 129.8ms लेता है, और पूरी config लगभग 250ms में, यानी काफ़ी मिलते-जुलते समय में
      compinit caching से औसतन करीब 5ms कम हुए, लेकिन completion छूट भी सकती है, इसलिए मुझे नहीं लगता कि यह मेहनत उतनी क़ीमती है
  • हाल में zsh startup इतना धीमा हो गया था कि लगभग अटका हुआ लगता था; मैं सटीक कारण नहीं ढूँढ पाया, लेकिन यह ज़रूर पुष्टि हुई कि critical path का ज़्यादातर हिस्सा compinit ले रहा था
    लेख में सुझाए गए तरीक़े से लगभग मिलता-जुलता caching लागू करके मैंने यह slowdown हटा दिया, और वह शानदार glob qualifier देखकर लगा कि अपना तरीका भी बेहतर करना चाहिए
    मुझे पता भी नहीं था कि ऐसा feature संभव है, और सच कहूँ तो थोड़ा संदिग्ध feature जैसा लगता है, फिर भी मैं इसे इस्तेमाल करूँगा
    पहले target path बनाते समय मैं काफ़ी भद्दे date -Id तरीके का इस्तेमाल करता था
    मुझे वे tools पसंद हैं जिनकी configuration zsh जैसी पूरी programming language में होती है, क्योंकि तब लेखक के caching feature जोड़ने का इंतज़ार किए बिना आप खुद उसे implement कर सकते हैं
    लगभग 20 साल से zsh इस्तेमाल कर रहा हूँ और कभी framework या plugin manager नहीं इस्तेमाल किया; लगता है ये ज़्यादातर styling के लिए ही चलन में हैं
    मैं अपने computing environment की सजावट की परवाह नहीं करता, तो इस मामले में किस्मत वाला हूँ; मेरा अपना prompt भी basic, छोटा और जानकारी देने वाला है, लेकिन बिल्कुल flashy नहीं, और मैं black background वाली default terminal theme इस्तेमाल करता हूँ

    • compinit caching परेशान करने वाली है क्योंकि cache बासी हो सकती है
      कई shell instances parallel में एक ही काम कर सकती हैं, और tmux में practice के लिए parallel instances चलाते समय यह अक्सर देखा
      ऊपर से home directory कई hosts, खासकर containers, के बीच shared भी हो सकती है, इसलिए आख़िरकार मैंने इसे lock file, expiry check, और zcompile condition handling वाले तरीक़े से व्यवस्थित किया
    • ZSH load time इतना खराब हो गया कि मैंने बस fish आज़मा लिया
      अफ़सोस है कि fish config भी धीरे-धीरे शायद उसी दिशा में चली गई है; सोमवार की छुट्टी में profiling करके देखूँगा कि lazy-loading techniques मेरे मामले में सच में फ़ायदेमंद हैं या नहीं
      लगता है ज़्यादातर धीमापन Starship के git module से आ रहा है, लेकिन कुछ aliases और helper functions भी हैं जिन्हें lazy-load किया जा सकता है
  • Emacs में बहुत पहले से background staging shell पहले से initialize करके रखी जाती है
    terminal खोलने का मतलब है उस buffer के लिए नई window खोलना और उसका नाम बदलना, और अगली बार के लिए shell फिर तैयार करने वाली thread को fork करना
    इसलिए startup latency नहीं होती
    याद है कि बहुत पहले reptyr के साथ Emacs के बाहर भी ऐसा कोई solution ज़बरदस्ती बनाने की कोशिश की थी, लेकिन आख़िरकार उसे जारी नहीं रखा, और अब ठीक से याद भी नहीं कि क्यों
    https://github.com/nelhage/reptyr

    • Android के zygote process जैसा लगता है, जो काफ़ी अच्छा है
  • इसी तरह टटोलते हुए पता चला कि zsh-abbr startup time के लगभग 100ms खा रहा है, लेकिन वह मेरे लिए ठीक है
    इधर-उधर से 10ms कम किए जा सकते हैं, लेकिन जो functionality खोएँगे उसे देखते हुए यह worth it नहीं लगता
    मैं लगभग 300ms startup time के साथ जी लूँगा; यह काफ़ी तेज़ है, और ऐसा भी कम ही होता है कि मुझे लगातार terminal खोलने पड़ें या तुरंत typing शुरू करनी हो
    फिर भी लेख अच्छा था, hyperfine के बारे में पता चला, और zsh startup files में कुछ झाँकने का मन हुआ

  • इसकी वजह से मैंने बहुत समय से टाला हुआ zshrc edit कर लिया, और अब यह 80ms तक आ गया है, जो बहुत अच्छा है

  • मेरी ज़िंदगी इतनी लंबी है कि मैं slow terminal झेल सकता हूँ, और कभी-कभी तो चाहता हूँ कि terminal और धीमा हो
    जैसे root console में असली execution से पहले default 5-second delay होती, ताकि typo होने पर Ctrl+C से cancel करने का समय मिल जाए, तो शायद अपने सिरफिरे जवान दिनों के कुछ दिन बचा लेता