2 पॉइंट द्वारा GN⁺ 2023-11-02 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • 1989 में Rob Pike के प्रोग्रामिंग के 5 नियमों पर लेख
  • नियम 1: यह मानकर मत चलो कि प्रोग्राम अपना ज़्यादातर समय कहाँ बिताएगा; bottleneck अप्रत्याशित जगहों पर आ सकते हैं। जब तक bottleneck सिद्ध न हो जाए, speed hack से बचो।
  • नियम 2: speed के लिए tuning करने से पहले हमेशा मापो। केवल तभी optimize करो जब कोड का कोई हिस्सा बाकी हिस्सों पर महत्वपूर्ण प्रभाव डालता हो।
  • नियम 3: जब n छोटा हो, तो जटिल algorithm धीमे होते हैं। अधिकांश मामलों में यही सच है। जटिल algorithm का उपयोग केवल तब करो जब n अक्सर बड़ा हो, और तब भी पहले नियम 2 लागू करो।
  • नियम 4: सरल algorithm और data structure बेहतर होते हैं। वे जटिल चीज़ों की तुलना में bug के प्रति कम संवेदनशील होते हैं और implement करना आसान होता है।
  • नियम 5: सही data structure प्रोग्रामिंग में निर्णायक है। अगर data अच्छी तरह व्यवस्थित है, तो algorithm स्वतः स्पष्ट हो जाएगा।
  • Pike के नियम 1 और 2, Tony Hoare के इस कथन को दर्शाते हैं: "premature optimization सभी बुराइयों की जड़ है।"
  • Ken Thompson ने Pike के नियम 3 और 4 को इस तरह दोबारा कहा: "जब संदेह हो, तो brute force का उपयोग करो।"
  • नियम 3 और 4, KISS (Keep It Simple, Stupid) design philosophy को लागू करते हैं।
  • नियम 5, Fred Brooks की 'The Mythical Man-Month' में कही बात से मेल खाता है, जिसे अक्सर संक्षेप में यूँ कहा जाता है: "smart object का उपयोग करने वाला stupid code लिखो।"

1 टिप्पणियां

 
GN⁺ 2023-11-02
Hacker News की टिप्पणियां
  • डेटा ही राज करता है” — इससे पूरी तरह सहमत हूं
    इसलिए LeetCode interviews मुझे हमेशा अजीब लगे। आम तौर पर फोकस algorithms पर होता है, जबकि असल में कई बार शुरुआत से ही इस तरह approach नहीं करना चाहिए और data structures ज़्यादा केंद्र में होने चाहिए
    हां, अगर algorithms की बिल्कुल समझ न हो तो edge cases या किसी खास वजह से किसी specific algorithm पर निर्भर होने की ज़रूरत कब है, यह समझ नहीं आ सकता। फिर भी algorithms अपेक्षाकृत कम समय में सिखाए जा सकते हैं, जबकि कौन-सा data structure इस्तेमाल करना चाहिए, इसकी समझ बनाना लोगों को ज़्यादा मुश्किल लगता है

    • मेरे अनुभव में भी सहमत हूं। Interviews में FizzBuzz जैसे algorithm check से आगे बढ़कर जब सीधे data structures, architecture और domain mapping पर बात शुरू होती है, तो मैंने देखा है कि interviewer कहीं ज़्यादा respect दिखाता है
      उस पल माहौल बदलकर “अच्छा, सच में एक senior engineer आया है” जैसा हो जाता है, technical problem पर ज़्यादा खुले तरीके से बात होती है और “coding आती है या नहीं” साबित करने वाला रवैया भी कम हो जाता है
      उल्टा, अच्छे बदलाव लाने, milestones हासिल करने और team collaboration में जिन teams के साथ सबसे ज़्यादा मुश्किल हुई, वे वे थीं जहां data structures और code architecture को ठीक से पकड़ने वाला कोई नहीं था। लगता है बहुत लोग इस सोच के आदी हो गए हैं कि framework सब कर देगा, और अगर न हुआ तो किसी ज़्यादा smart व्यक्ति का बनाया plugin या middleware इसे solve कर देगा
      जो engineer data structures से बचता है, वह अपने ही पैर पर कुल्हाड़ी मारता है, और सबसे उपयोगी tools में से एक छोड़ देता है; इसकी सीमाएं रोज़मर्रा में दिखने लगती हैं
    • Competitive programming contests की तैयारी कर रहे अपने भांजे/भतीजे की मदद करते हुए लगा कि ज़्यादातर problems में data को सही data structure में transform करना solution का बड़ा हिस्सा था
      उदाहरण के लिए code का बड़ा हिस्सा weighted directed acyclic graph (DAG) में longest path खोजने के लिए इस्तेमाल हो सकता है, लेकिन core बात यह समझना था कि problem को weighted DAG के रूप में represent किया जा सकता है। अगर यह नहीं दिखता, तो problem फिर भी solve हो सकती है, लेकिन solution कहीं ज़्यादा धीमा और जटिल होगा
    • आम LeetCode problems भी असल में data structures पर ही focus करती हैं। क्योंकि candidate को problem और solution की pattern matching करते समय अपने दिमाग में data structures की list रखनी पड़ती है
      Interviewer पहले से यह नहीं बताएगा कि priority queue, adjacency matrix, trie वगैरह इस्तेमाल करना है। अगर आप अटकें तो hint मिल सकता है, लेकिन बहुत ज़्यादा hand-holding को strong hiring signal मानना मुश्किल है
    • “Flowcharts दिखाइए लेकिन tables छिपाइए, तो मैं लगातार उलझा रहूंगा। Tables दिखाइए, तो flowcharts देखने की ज़रूरत नहीं पड़ेगी। बात अपने-आप साफ हो जाएगी”
    • दोनों में से एक चुनना हो तो दूसरे के बारे में मोटा-मोटी समझ तो चाहिए ही, ऐसा लगता है। अगर data को कैसे access करना है इसकी बिल्कुल समझ न हो, तो कौन-सा data structure इस्तेमाल करना चाहिए यह जानना मुश्किल है
  • “Fancy algorithms तब धीमे होते हैं जब n छोटा हो, और n आम तौर पर छोटा ही होता है” — इस बात के संदर्भ में, हाल के project में जो महसूस हुआ वह यह है कि बड़ा n आपकी सोच से कहीं ज़्यादा बड़ा हो सकता है
    “एक लाख operations करने होंगे, इसलिए optimize करना ही पड़ेगा” सोचना आसान है, लेकिन computers तेज़ हैं और एक लाख multiplications जैसी चीज़ आम तौर पर इतनी जल्दी हो जाती है कि उस पर गहराई से सोचने की ज़रूरत नहीं पड़ सकती
    मतलब यह नहीं कि बिल्कुल सोचना ही नहीं चाहिए, लेकिन modern hardware कितना पागलपन की हद तक तेज़ है, यह अक्सर चौंकाता है

    • इस बात से strongly सहमत होना मुश्किल है। Quadratic time algorithms उस तरह की चीज़ हैं जो अनपेक्षित समय पर काट सकती हैं
      मैंने गलती से quadratic time बन गए code की वजह से production outage होते देखा है, और भले ही 99% users हमेशा छोटे n का इस्तेमाल करें, कुछ users अक्सर बड़े n से टकराकर बहुत slow app experience कर सकते हैं
      ज़्यादातर मामलों में, common case में थोड़ा slow और implementation थोड़ी complex होने पर भी मैं quadratic time से बेहतर algorithm चुनना चाहूंगा। आम slow paths optimize हो जाते हैं, लेकिन rare slow paths को developer खुद hit नहीं करता, इसलिए वे छूट जाते हैं या production में फटते हैं
      बेशक अगर algorithm बहुत complex हो तो simple quadratic time implementation चुना जा सकता है, लेकिन default मैं संभव हो तो quadratic से कम रखना चाहूंगा। इस बारे में लिखा मेरा लेख भी है: https://kevincox.ca/2023/05/09/less-than-quadratic/
    • Memory hierarchy भी यहां असर डालती है। कई fancy algorithms में reference locality खराब होती है और extra branching जुड़ती है
      इसलिए 40 साल पहले, जब CPU memory से इतना तेज़ नहीं था और consumer hardware पर branch prediction failures की इतनी चिंता नहीं की जाती थी, तब यह बात ज़्यादा सही बैठती होगी
    • LeetCode interview problems में 100,000 items वाली list को कई बार iterate करते देखता रहता हूं। यह optimal नहीं हो सकता, लेकिन असली production time के हिसाब से उसके तुरंत बाद की जाने वाली network call के मुकाबले 100,000 items पर iteration कुछ भी नहीं है
      हर interview में hiring manager यह चाहता है, लेकिन ऐसा होता है कि production के घाव अभी न झेल चुके LeetCode beginner decision लेने से इनकार कर देते हैं
    • इस topic का representative reference Scalability! But at what COST? है
      https://www.frankmcsherry.org/assets/COST.pdf
    • 2000 के दशक की शुरुआत में जब मैंने एक game company में अपनी पहली ढंग की programming job शुरू की, तो technical director ने सलाह दी थी: “अगर items की संख्या लगभग 10,000 है, तो optimize मत करो”
      पिछले 20 सालों में computer performance में सुधार को देखते हुए, उस threshold को 100,000 तक बढ़ाना काफ़ी उचित लगता है
  • “premature optimization is the root of all evil” वाली मशहूर कहावत असल में Tony Hoare की नहीं, बल्कि Donald Knuth की कही हुई है, और इसे अक्सर संदर्भ के बिना optimization के खिलाफ सामान्य बयान की तरह इस्तेमाल किया जाता है
    पूरा वाक्य है: “हमें छोटी-छोटी efficiency, यानी कहें तो 97% मामलों को भूल जाना चाहिए। premature optimization is the root of all evil. लेकिन अहम 3% में मौके नहीं चूकने चाहिए”
    असल बात यह है कि optimization वहीं करें जहाँ उसका असर हो

    • Knuth इसे Hoare का कथन बताते हैं, और Hoare इसे Knuth का, इसलिए बात इस पर आ जाती है कि किस पर भरोसा करें। शायद दोनों को श्रेय देना ही सबसे ठीक होगा
      मुमकिन है Tony ने पहले कहा हो और Knuth ने उसे तराशकर प्रकाशित किया हो। जरूरी संदर्भ देने वाला लंबा quote साथ रखना हमेशा अच्छा है
    • यह भी अक्सर भुला दिया जाता है कि यह quote 1970s के आखिर का है। लगभग 50 साल पहले का
      उस समय programming आज से काफी अलग थी। तब “premature optimization” का मतलब “बस scalable popular library इस्तेमाल कर लो” नहीं था, बल्कि “ऐसा समझ से बाहर bit-manipulation algorithm इस्तेमाल करो जो सिर्फ इसी hardware पर चले” के ज्यादा करीब था
    • मुझे नहीं लगता कि लंबा quote कोई खास अतिरिक्त संदर्भ देता है। अगर आपने मापकर महत्वपूर्ण 3% ढूँढ लिया है, तो वह स्थिति अब premature नहीं रह जाती
      “premature optimization is the root of all evil” में यह अर्थ पहले से शामिल है; कहावत “optimization is the root of all evil” नहीं है
    • बहुत लोग इस बात को सिद्धांत की तरह मानकर efficient methods सीखते ही नहीं
      company के data structures/algorithms interviews में मैंने गिनती से बाहर frontend developers को bubble sort को सबसे अच्छा विकल्प कहते देखा है। मौके पर derivation करवाने की जरूरत नहीं, बस कुछ तरीके जानना और समस्या के हिसाब से अच्छा विकल्प बता देना काफी है
      अगर “premature optimization मत करो” को इतनी चरम सीमा तक जीते हैं कि efficient methods तक नहीं जानते, तो यह कैसे पता चलेगा कि important हिस्सा कहाँ है
    • इस संदर्भ में यह optimization के खिलाफ सामान्य बयान की तरह इस्तेमाल हुआ नहीं लगता
  • “data structures ही core हैं” वाली बात databases में दोगुनी महत्वपूर्ण है
    जो लोग DB को बेवकूफ bit storage या object definitions का 1:1 reflection भर समझकर इस्तेमाल करते हैं, वे अक्सर तब हैरान होते हैं जब DB इसे personal लेता है और performance खराब कर देता है
    अगर मुझे ORM-generated DB schema फिर से देखना पड़ा, तो वह reunion बहुत जल्दी होगा

    • मुझे लगता है कि ज्यादातर ORM वही schema generate करते हैं जो उनसे मांगा जाता है। ORM इस्तेमाल करने से हाथ से बनाए schema की तुलना में अपने-आप ज्यादा खराब database design नहीं बनता
      समस्या यह है कि कुछ, या कई, developers SQL नहीं जानते, और ORM इस्तेमाल करने के लिए जरूरी DB knowledge भी नहीं रखते
      ORM काफी leaky abstraction है, जिसके नीचे क्या है यह जानना जरूरी है। यह समझ लें तो ज्यादातर ORM से भी ठीक-ठाक schema बनाया जा सकता है
    • इसमें Conway's Law भी जोड़ी जा सकती है। इसका मतलब है: “जो organizations systems design करती हैं, वे ऐसा design बनाती हैं जो उनकी communication structure की नकल करता है”
      data structures को अच्छी तरह organize करने और design बदलने पर भी वैसा बनाए रखने के लिए data और code को organization level पर अलग करना होगा
      DB schema design, use cases और उनके बीच की mapping को बाकी implementation से अलग करना चाहिए, और इसी group को integrity checks आदि भी लिखने चाहिए। अगर organization structure data और code को अलग नहीं करता, तो code और data को अलग करना मुश्किल है
    • stored procedures जीतते हैं
  • मेरा एक extra rule यह है कि छोटी-छोटी performance waste जमा होकर, भले ही अलग-अलग मामूली लगें, आखिरकार program को धीमा बना देती हैं
    अगर complexity, readability, maintainability और implementation cost पर असर नहीं पड़ता, तो performance को यूँ ही छोड़ नहीं देना चाहिए। बाकी शर्तें लगभग समान हों तो दो विकल्पों में धीमे वाले को चुनना ठीक नहीं है
    और अगर मान लें कि n छोटा है, तो लगभग कुछ भी चल जाता है। लेकिन अगर ऐसा code लिखते हैं जो n के 100 से कम होने पर ठीक चलता है और 10000 से ऊपर टूट जाता है, जैसे O(n²), तो साफ limit लगानी चाहिए। छोटा n मानने वाली धारणा टूटे तो AWS bill bomb या अटके हुए program से बेहतर है कि जोर से error दे

    • यहाँ rules 1 और 2 लागू होते हैं
  • इन guidelines में से काफी आखिरकार over-engineering रोकने की strategy पर आकर टिकती हैं
    मेरे अनुभव में premature optimization सबसे महंगे traps में से एक है। संभावित समस्या को बहुत जल्दी bypass करने पर वह assumption validate नहीं होती, और अगली team को अनावश्यक complexity सुलझाने के लिए महंगा solution बनाना पड़ता है
    सीखा हुआ approach यह है: optimization estimates पर निर्भर करती है, और शुरुआती estimates अक्सर गलत होते हैं
    यह भी समझ आया कि लोगों को जरूरत से ज्यादा complex code बनाने से रोकने के लिए ego management और psychology की समझ काफी महत्वपूर्ण है

    • मैं अक्सर इसे ऐसे कहता हूँ: “जो समस्या आपके पास है उसे हल करो। वह समस्या मत हल करो जो आपको लगता है कि आपके पास है”
    • यह concept lean और Six Sigma की waste identification से भी जुड़ता है
      overproduction को आम तौर पर सबसे खराब waste माना जाता है, क्योंकि यह न सिर्फ ऐसी चीज बनाता है जिसकी जरूरत नहीं, बल्कि वह effort भी खा जाता है जो वास्तव में जरूरी चीज पर लग सकता था। over-engineering भी ऐसी ही है
    • एक कदम और अंदर जाएँ तो over-engineering इसलिए पैदा होती है क्योंकि लगता है कि शायद बाद में complexity की जरूरत पड़ेगी और तब system को expand करना ज्यादा मुश्किल या risky होगा
      उदाहरण के लिए, सिर्फ 100 users होने पर भी यह सोचकर microservices architecture से शुरू करना कि कभी 1 million users हो गए तो monolith को redesign करना मुश्किल होगा
      इसलिए पहले यह address करना चाहिए कि समय के साथ code कम flexible क्यों हो जाता है
    • error handling में दिखावा न करें; जल्दी और सरल तरीके से fail करना बेहतर है
  • कुल मिलाकर अच्छे नियम हैं, लेकिन असल में नियम 1 वैसा का वैसा लागू नहीं होता
    शुरुआत करते समय यह परिकल्पना चाहिए कि bottleneck क्या बनेगा। ऐसा हमेशा संभव नहीं होता कि XYZ implement करने के बाद माप लें कि क्या slow है और उसे ठीक कर दें। X, Y, Z आपस में जुड़े होते हैं, इसलिए Y को तेज़ बनाने के लिए कभी-कभी X और Z को किसी खास तरीके से बनाना पड़ता है, और कभी-कभी पहले से पता होता है कि Y bottleneck बनेगा
    बाद में मापकर यह जान भी जाएँ कि क्या slow है, तब भी तेज़ बनाने के approach पर दांव लगाना पड़ता है। दांव जितना ज़्यादा informed हो, उतना अच्छा
    अच्छे programmer मापते हैं, लेकिन वे यह भी predict कर सकते हैं कि क्या slow होगा, किसमें bugs ज़्यादा होंगे और क्या ज़्यादा memory लेगा, इसलिए उन्हें कम iterations करनी पड़ती हैं। performance behavior predict नहीं किया जा सकता—इसे नियम की तरह कहना, अच्छे programmers के जमा किए अनुभव और कौशल को नज़रअंदाज़ करना है

    • नियम 1 उन लोगों के लिए लोहे का नियम है जो इस पर भरोसा नहीं करते, और जो भरोसा करते हैं उनके लिए ढीला guideline
      क्योंकि नियम 1 का पालन करने की प्रक्रिया ही bottlenecks का अंदाज़ा लगाने वाली अच्छी intuition के लिए ज़रूरी अनुभव और empirical background पाने का सबसे अच्छा तरीका है
    • slow लगने वाले algorithm को spike implementation से verify किया जा सकता है। आम तौर पर slow algorithm implement और test करना आसान होता है
      अगर speed prediction गलत निकला, तो project की पूरी उम्र तक बेवजह complex code ढोना पड़ता है
      लोग algorithm की speed का अंदाज़ा अक्सर गलत लगाते हैं। अगर computer अपना 99% समय DB server से n लाने में लगाता है, तो O(n) और O(n²) अक्सर real time में एक जैसे दिख सकते हैं
      C में लिखा algorithm भी equivalent Python code से slow हो सकता है, क्योंकि bytecode compiler ने कोई smart काम किया हो सकता है
      मैंने legacy code को fast बनाने का काम काफ़ी किया है; आम तौर पर यह सोच से कहीं आसान होता है, और ऐसे कारणों से slow होता है जो original author को obvious नहीं थे। असल में, codebase इतना complex हो चुका होता है कि मूल लेखक उससे reasoning नहीं कर पाता, इसलिए वह slow रहता है। मेरे पास “बहुत slow” का एक concrete example होता है, इसलिए उसे run करके slow points observe करते हुए debug करना आसान होता है
    • यह शायद पूरे original text पर प्रतिक्रिया नहीं है। वहाँ कहा गया है कि bottleneck पता चलने से पहले speed hacks न डालें। आपने जो स्थिति बताई है, वह अलग है
      अगर आप बहुत सारे physical objects वाला video game बना रहे हैं, और अनुभव से पक्का जानते हैं कि collision detection बड़ा issue होगा, तो game और system को उसी के इर्द-गिर्द design करना speed hacking नहीं है
      अगर काम ऐसा है जहाँ performance बड़ा concern बनने वाला है, तो मापना स्वाभाविक है। यह जाँचने के लिए नहीं कि वह concern है या नहीं, बल्कि यह देखने के लिए कि आप उस concern को कितनी अच्छी तरह handle कर रहे हैं
    • कोई concrete example जानना चाहूँगा। ज़्यादातर मामलों में सचमुच फर्क पड़ता है या नहीं, इस पर शक है
      अगर नई requirements के लिए नया system बना रहे हैं, तो कई बार बस शुरू कर देना ठीक लगता है। बनाइए, test और measure कीजिए, फेंक दीजिए या refactor कीजिए, और repeat कीजिए
      Rust को उदाहरण लें तो यह draft language और OCaml में बने compiler से शुरू हुआ और फिर iterate हुआ। भले ही पता था कि किसी दिन OCaml से self-hosting पर जा सकते हैं, मुझे नहीं पता कि उससे बड़ा फर्क पड़ता
    • अगर developers इतना अच्छे से predict कर सकते हैं कि क्या slow होगा, तो developer-led startups की success rate 100% होनी चाहिए, ऐसा लगता है
      अगर users ही नहीं हैं, तो घंटों लगने वाला function भी उस function की तुलना में अभी भी काफी fast है जिसे optimize करने पर milliseconds लगते हैं। मुझे नहीं पता कि किसी ने यह साबित किया है कि वह ऐसे predictions सही-सही कर सकता है
  • नियम 5 के विरोध में, simple data पर complex algorithms बड़ा performance gain दे सकते हैं, obstacles हटाते हैं और उल्टा चीज़ों को simplify भी कर सकते हैं
    उदाहरण के लिए BinaryTree object के बजाय sorted array में binary search करें, तो merge करना concat के बाद sort भर रह जाता है और simple हो जाता है; pointers नहीं होते, इसलिए serialization आसान हो जाता है, और कुछ मामलों में serialization की ज़रूरत ही नहीं रहती। array disk या memory में, या mmap के ज़रिये दोनों में हो सकता है; RAM से बड़े data को भी handle कर सकता है; और file या mapping की ओर इशारा करके सीधे run करने वाला cold start भी संभव है। इसमें cache-oblivious गुण भी हैं
    Huffman coding भी उदाहरण है। University में आम तौर पर tree-based algorithm और O(n log n) complexity के रूप में सीखा होगा, लेकिन मुझे नहीं पता था कि in-place array-based तरीके से Huffman tree को linear time में construct करने का तरीका भी है
    बेशक 99% समय backend microservices बनाते हुए standard collection data structures इस्तेमाल होते हैं। लेकिन अगर काम पर big data का काम कर रहा हूँ, तो उस समय trend में चल रहे MapReduce परिवार को adopt करने के बजाय local बड़े disks वाली single machine पर process करना कहीं ज़्यादा पसंद करूँगा

    • binary search को मैं कोई शानदार algorithm नहीं मानता। modern sort function शानदार चीज़ है, और उसमें subtle bugs हो सकते हैं, इसलिए आम developer को उसे खुद नहीं बनाना चाहिए। quicksort में भी traps हैं
      Rob Pike शायद पहले code की profiling करने को कहते, और फिर देखते कि fancy code या alternative data structure सचमुच तेज़ है या नहीं
    • यह rebuttal जैसा नहीं लगता। Pike की advice के नज़रिए से “sorted array में binary search” और “BinaryTree object” उसी data structure के दो अलग implementations भर हैं
    • यह नहीं भूलना चाहिए कि 99% समय developer ही सबसे महंगा resource होता है। maintainability और market में जल्दी launch करना आम तौर पर कहीं ज़्यादा महत्वपूर्ण होते हैं
  • यह लेख मैंने 10 साल से भी पहले पहली बार cat-v पर पढ़ा था, और design व complexity को approach करने और उसके बारे में सोचने के तरीके पर इसका अमिट असर पड़ा
    http://doc.cat-v.org/bell_labs/pikestyle

  • मूल नियम “data structures ही core हैं” से यह घटकर “smart objects इस्तेमाल करने वाला dumb code लिखो” कैसे बन जाता है, समझ नहीं आता
    “smart objects” phrase बहुत खराब लगा, और मूल नियम लंबा होने पर भी कहीं बेहतर है

    • Rob Pike भी शायद इस बात से सहमत होंगे कि “smart objects” सोचने का गलत तरीका है: https://commandcenter.blogspot.com/2012/06/less-is-exponenti...
    • “smart” logic को higher level पर ले जाना समझने, test करने और बदलने में आसान होता है। मेरे हिसाब से smart objects को आपस में cohesive बनाना कहीं ज़्यादा कठिन है
    • इसका मतलब यूँ समझें कि अच्छी तरह structured objects से naturally निकलने वाला code लिखें