4 पॉइंट द्वारा GN⁺ 2024-10-19 | 1 टिप्पणियां | WhatsApp पर शेयर करें

Go की concurrency और parallelism का उपयोग

  • संख्यात्मक गणना क्षमता को बेहतर बनाने के लिए Go की concurrency और parallelism का उपयोग करने वाले एक प्रोजेक्ट का परिचय।
  • SIMD(Same Instruction Multiple Data) निर्देशों का उपयोग करके हार्डवेयर स्तर पर parallel गणना की जा सकती है।
  • Go का compiler SIMD का उपयोग नहीं करता, और उपयुक्त general-purpose SIMD package न मिलने पर स्वयं package विकसित करने का निर्णय लिया गया।

Plan9 assembly भाषा

  • Go अपनी खुद की Plan9 assembly भाषा का उपयोग करता है, जो किसी विशेष platform के instructions और registers को थोड़ा संशोधित करके इस्तेमाल करती है।
  • x86 Plan9 और ARM Plan9 एक-दूसरे से अलग हैं।
  • Plan9 के सरल उदाहरणों के माध्यम से इसकी बुनियादी उपयोग-विधि समझाई गई है।

Plan9 उदाहरण

  • AddInts_amd64.s फ़ाइल और main.go फ़ाइल के माध्यम से Plan9 में बुनियादी function declaration और उपयोग का तरीका समझाया गया है।
  • Go की calling convention के अनुसार function arguments और return values को stack में store करने का तरीका बताया गया है।

package design योजना

  • arithmetic और bit operation SIMD कार्यों के लिए एक पतली abstraction layer प्रदान करने वाला package डिज़ाइन किया गया है।
  • architecture-विशिष्ट Plan9 implementation शामिल करने वाला internal package बनाया जाता है, और initialization function के माध्यम से इसे सेट किया जाता है।

SIMD उदाहरण

  • x86 SIMD Plan9 function के उदाहरण के माध्यम से SIMD के उपयोग का तरीका समझाया गया है।
  • Supported_amd64.s और AddFloat32_amd64.s फ़ाइलों के जरिए SSE support की जाँच और float32 addition operation करने का तरीका दिखाया गया है।

performance और भविष्य

  • Go software implementation और Plan9 SIMD implementation के बीच performance अंतर दिखाने वाले chart से लगभग 200-450% speed improvement की पुष्टि होती है।
  • आशा है कि यह मेमो Plan9 और SIMD का उपयोग करने वाले प्रोजेक्ट्स के लिए प्रेरणा देगा।

# GN⁺ का सार

  • यह लेख Go की concurrency और parallelism का उपयोग करके performance को अधिकतम करने के तरीकों का परिचय देता है।
  • Plan9 assembly भाषा और SIMD निर्देशों का उपयोग करके हार्डवेयर स्तर पर parallel गणना करने के तरीके समझाए गए हैं।
  • यह लेख Go developers के लिए Plan9 और SIMD के उपयोग की संभावनाएँ दिखाता है और performance सुधार के लिए नए approaches तलाशने में उपयोगी हो सकता है।
  • समान कार्यक्षमता वाले प्रोजेक्ट्स के रूप में Rust की SIMD support libraries या C++ की SIMD-संबंधित libraries की सिफारिश की गई है।

1 टिप्पणियां

 
GN⁺ 2024-10-19
Hacker News की राय
  • Go assembly के बारे में कुछ बातें साफ़ कर दूँ: amd64 पर वे int असल में 64-bit हैं
    int32 इस्तेमाल करने पर parameter list में word alignment हो जाता है, लेकिन इसमें एक फंदा है। 64-bit सिस्टम पर return value हमेशा doubleword-aligned offset से शुरू होती है
    NOSPLIT textflag.h में define है, जिसे Go compiler अपने-आप उपलब्ध कराता है। हालांकि जितना मैंने पढ़ा है, NOSPLIT शायद सिर्फ़ runtime.XX functions में ही माना जाता है, इसलिए यहाँ यह कुछ नहीं करता, और ज़रूरी भी नहीं है
    NOSPLIT का मतलब है कि compiler वह code insert न करे जो जाँचता है कि stack overflow हो सकता है या नहीं और splitting की ज़रूरत है या नहीं। जिस function को stack space की ज़रूरत नहीं है, उसके लिए यह technically अनावश्यक है, और मूल रूप से यह इसलिए है कि stack splitting की जाँच करने वाले function के अंदर ही वह checking code inject न हो जाए

  • जिन्हें यह जानना हो कि 4 “NOSPLIT” को दर्शाता है और क्यों इसे ज़रूरी बताया गया है, उनके लिए: आम तौर पर frame size (NOSPLIT के बाद वाला parameter) के बाद argument size आता है, और दोनों को minus sign से अलग किया जाता है
    यह subtraction नहीं, बस एक अजीब syntax है। frame size $24-8 का मतलब है कि function के पास 24-byte frame है, और उसे caller frame में मौजूद 8-byte arguments के साथ call किया जाता है
    अगर TEXT में NOSPLIT specify नहीं करते, तो argument size देना अनिवार्य है। Go prototype वाले assembly function के मामले में go vet जाँचता है कि argument size सही है या नहीं
    स्रोत: https://go.dev/doc/asm

  • “Go अपनी internal assembly language, Plan9, इस्तेमाल करता है” कहा गया है—क्या वह language सच में इसी नाम से जानी जाती है?

    • नहीं। यह बस Go assembly है
      Syntax Plan 9 से निकला है, लेकिन हम इसे Go assembly कहते हैं
      https://go.dev/doc/asm देखें
    • वाजिब सवाल है। शुरू में मैंने भी इसे सही ही मान लिया था। लगा कि जिसने इस विषय पर research किया है, वह ऐसी बात गलत नहीं करेगा, और अगर project के बारे में थोड़ा जानते हों तो यह नाम भी कुछ हद तक ठीक लगता है
      लेकिन जितना और खोजा, उतना लगा कि यह LLM hallucination जैसा है
      Assembly format का document इसे कोई ठीक-ठाक नाम नहीं देता, बस go assembler कहता है
      इस hallucination का स्रोत शायद पहला paragraph है: “assembler Plan 9 assembler की input style पर आधारित है और… यह document उस syntax का सार, differences, और Go के साथ interact करने वाला assembly code लिखते समय की peculiarities समझाता है”
    • इसका कोई अलग नाम नहीं है। Plan 9 एक operating system है, और इस assembly syntax style की उत्पत्ति उसी operating system में इस्तेमाल होने वाले assembler से हुई है
      यह कहने जैसा है कि “GNU Compiler Collection अपनी internal assembly language Unix इस्तेमाल करता है”
  • अगर आप सोच रहे हैं कि Go team ने ऐसा dedicated assembly format क्यों चुना, तो Rob Pike ने 2016 की एक presentation में Go assembler design पर बात की थी [1][2]
    मुख्य बात शायद यह observation थी कि ज़्यादातर assembly languages काफ़ी मिलती-जुलती हैं, इसलिए एक common assembly language बनाई जाए जो “machine के सबसे निचले level से बात करते हुए भी नया syntax सीखने की ज़रूरत न रहने दे”
    इससे नए architecture के instruction manual PDF को input के रूप में लेकर काम करने वाला assembler auto-generate करना भी संभव हो जाता है
    [1]: https://www.youtube.com/watch?v=KINIAgRpkDA
    [2]: https://go.dev/talks/2016/asm.slide#1

    • असल में इसका असर हुआ। Go ने यह expectation बनाई कि नई programming language में cross-compilation basic capability होनी चाहिए, और उस समय इसे अच्छी तरह करने वाली languages बहुत कम थीं
    • संदर्भ के लिए, SpiderMonkey भी लगभग 25 साल से काफ़ी मिलता-जुलता काम कर रहा है, और मुझे लगता है बाकी JavaScript virtual machines भी ऐसा ही करती होंगी
  • “slice पर SIMD operations करने वाले functions की ज़रूरत थी” कहा गया था, लेकिन मैं सोच रहा था कि असल में इसका उपयोग कैसे होता है
    पूरे लेख में खोजने पर भी slice पर किए जाने वाले operations कहीं नहीं मिले
    सुधार: linked document में मिला: https://pkg.go.dev/github.com/pehringer/simd#pkg-index
    मूल रूप से अगर आपके पास 2 slices हैं जिन्हें add करना है, तो for loop की जगह SIMD से parallel process करके simd.AddInt32(slice1, slice2, result) जैसा इस्तेमाल कर सकते हैं

  • Go द्वारा support किए जाने वाले processors के संबंध में यह section relevant है (1). Basic x64 support में SSE और SSE2 शामिल हैं
    हालांकि मुझे नहीं पता कि Go compiler वास्तव में उन्हें generate करता है या नहीं। gcc जैसे बहुत complex compiler, जो performance को सर्वोपरि मानते हैं, उनसे अलग Go compiler Wirth-style (2) में simple और fast compiler को प्राथमिकता देता है
    (1) https://go.dev/wiki/MinimumRequirements#amd64
    (2) https://irreal.org/blog/?p=7075
    https://smartgo.blog/2024/01/06/niklaus-wirth/

    • असल में, मेरा मानना है कि 2008 के आसपास के बाद की सभी chips SSE4.1 support करती हैं
  • लगता है लेखक ने इस हिस्से में confusion किया है, इसलिए reference link छोड़ रहा हूँ: https://en.wikipedia.org/wiki/Plan_9_from_Bell_Labs

  • मैं भी यही कहने वाला था कि यह कोड को LLM द्वारा गलत समझने का नतीजा लगता है
    Plan 9 शब्द के बारे में जानना और assembly तक गहराई में जाना, लेकिन फिर भी यह न समझना कि वह किस दुनिया में कदम रख रहा है—इसे किसी और तरह से कल्पना करना मुश्किल है। दूसरों को भी ऐसा ही लगा, यह देखकर समझ आया
    अगर यह सही है, तो लेखक को शर्मिंदा या “पकड़ा गया” महसूस करने के बजाय ईमानदार होना चाहिए। तभी हम भी सीख सकते हैं। मैं इस तरह के “LLM exposure” टाइप के मामलों पर भरोसा बनाना चाहता हूं, लेकिन चाहे वे कितने भी स्पष्ट दिखें, लोग शायद ही कभी मानते हैं
    बेशक यहां यह स्पष्ट नहीं है, बस बहुत जल्दबाजी में की गई और judgmental अटकल है

    • सच कहूं? नहीं। यह सोचने वाला अहंकार शर्म की बात है कि LLM उस काम को न करने का shortcut है जो उसे ठीक से करना चाहिए था
      ऐसी चीजें सच में गुस्सा दिलाती हैं
  • यह मेरे स्तर से थोड़ा ऊपर था, लेकिन लेख जिस तरह पाठक को साथ लेकर चलता है, वह अच्छा लगा
    ऐसा प्रयास पहली बार तो नहीं हो सकता, है ना? SIMD के लिए तरसते literally दर्जनों Gopher होंगे। क्या ज्यादा आम pattern CGO इस्तेमाल करना है?

    • cgo की समस्या यह है कि function call overhead बड़ा होता है। इसे केवल काफी बड़े काम के chunks के लिए ही इस्तेमाल करना बेहतर है
      Go में assembly function call करना कहीं ज्यादा सस्ता है
      https://pkg.go.dev/github.com/grailbio/base/simd पर मैंने इसी दिशा में जो काम किया है, वह मौजूद है
    • मुझे लगता है लोग पहले से ही निश्चित रूप से कोशिश करते रहे होंगे। लगभग 10 साल पहले की बात है, याद है कि मेरा भाई Go में SIMD library बनाने की कोशिश कर रहा था और हम call पर बात कर रहे थे (शायद Skype?)
      अगर मेरी याद सही है, उस समय कई AVX instructions Go के Plan 9 assembler में encode तक नहीं होते थे, इसलिए उन्हें bytes के रूप में सीधे encode करना पड़ता था [0]
      मैंने जो सबसे complete library देखी है, हालांकि इस्तेमाल नहीं की, वह CGO को आंशिक रूप से इस्तेमाल करते हुए उस overhead से बचने के लिए एक साफ hack इस्तेमाल करती है [1]
      [0]: https://github.com/slimsag/rand/blob/f1e8d464c0021a391d5cd64...
      [1]: https://github.com/alivanz/go-simd/
    • ऐसी चीज को standard library में जोड़ने का proposal reject हो गया था, लेकिन background समझने में यह मदद करता है: https://github.com/golang/go/issues/53171
  • Go assembly program लिखना है तो Avo(https://github.com/mmcloughlin/avo) देखने की सलाह दूंगा
    यह type safety देता है और valid assembly output हो रही है या नहीं, इसके लिए कुछ checks करता है। यह registers को dynamically allocate कर सकता है, और stack व frame size जैसी चीजें खुद calculate करने की जरूरत भी खत्म कर देता है
    यह calling convention details भी handle कर सकता है, ताकि arguments को मनचाहे register या location में आसानी से load किया जा सके
    हाल ही में मैंने Go की crypto library में मौजूद पूरी amd64 assembly को Avo पर port किया है, और ऐसे कामों के लिए यह बहुत उपयोगी library है