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

कोड की गति बढ़ाएँ: AMD64 पर 16 बाइट से बड़े struct पास न करें

  • Neat भाषा के प्रदर्शन को बेहतर बनाने के लिए array को एक struct parameter की जगह तीन pointer parameter के रूप में पास करने के तरीके में बदलाव किया गया।
  • Neat array, D भाषा के array से धीमा क्यों था, इसका कारण यह था कि 24-बाइट आकार का array 16 बाइट से बड़ा होने के कारण parameter पास करने के अलग तरीके का उपयोग करता था।
  • SystemV AMD64 ABI specification के अनुसार, 16 बाइट से बड़े सभी struct pointer के माध्यम से पास किए जाते हैं।

benchmark के ज़रिए समस्या की पुष्टि

  • benchmark के माध्यम से struct पास करने के तरीके और individual field पास करने के तरीके के बीच प्रदर्शन अंतर की पुष्टि की गई।
  • struct पास करते समय stack पर allocate और copy करने की प्रक्रिया की आवश्यकता होती है, जबकि individual field पास करते समय वे सीधे SSE register के माध्यम से पास किए जाते हैं।
  • individual field पास करने का तरीका struct पास करने की तुलना में लगभग 2 गुना तेज़ प्रदर्शन दिखाता है।

भाषा डिज़ाइनर का चयन

  • C API को कॉल करते समय C ABI का पालन करना होता है, लेकिन internally इस्तेमाल होने वाले high-level type को struct के रूप में व्यक्त करना ज़रूरी नहीं है।
  • भाषा डिज़ाइनर यह तय कर सकते हैं कि array, tuple, union type आदि कैसे पास किए जाएँगे।
  • 16 बाइट से बड़े type को individual field के रूप में पास करना प्रदर्शन सुधार में मदद कर सकता है।

GN⁺ की राय

  • यह लेख software optimization में रुचि रखने वाले developers के लिए बहुत उपयोगी है।
  • खास तौर पर, यह दिखाता है कि performance-sensitive application विकसित करते समय struct का आकार और उसे पास करने का तरीका महत्वपूर्ण प्रभाव डाल सकते हैं।
  • भाषा डिज़ाइनर या API developer इस जानकारी का उपयोग करके प्रदर्शन सुधार के अवसर पा सकते हैं।

1 टिप्पणियां

 
GN⁺ 2024-01-06
Hacker News टिप्पणियाँ
  • SysV amd64 ABI समस्या के संदर्भ में, भाषा का आंतरिक ABI, SysV के बजाय कुछ और रखा जा सकता है। जब तक वह SysV C callers के सामने expose न हो, आप अपनी पसंद का calling convention इस्तेमाल कर सकते हैं। NeatLang का अंतर केवल LLVM calling convention बदलने की तुलना में कहीं अधिक जटिल लगता है, और लेखक शायद C programs के लिए types को एक निश्चित calling convention के साथ expose करना चाहते हों।
  • अक्सर argument passing की लागत को लेकर समझ की कमी होती है, और इस पर लिखा गया लेख उपयोगी है। उदाहरण के लिए, Google में 24-byte objects को by value pass करने की प्रथा profiler में दिखाई नहीं देती, लेकिन हर function में इसकी लागत पड़ती है।
  • x64 पर जाते समय, vec3 object (3xfloat) के 12 bytes से बढ़कर 16 bytes होने को लेकर चिंता हुई, इसलिए graphics engine का benchmark किया गया। पता चला कि 16 bytes का उपयोग 8-byte reads के साथ aligned होने के कारण तेज़ था। नतीजतन, vec3 को vec4 की तरह इस्तेमाल किया गया। हमेशा end-to-end benchmarking करने की सलाह दी जाती है।
  • registers में पहले से लोड किए गए arguments, stack writes की तुलना में बेहतर प्रदर्शन देते हैं, और stack manipulation, heap allocation की तुलना में तेज़ होती है। यही कारण है कि बहुत सारे global variables वाला जटिल code तेज़ चल सकता है, जबकि elegant recursive functions या tuple/struct/list arguments धीमे हो सकते हैं। पहले वाले को dense assembly loops में optimize करना आसान होता है।
  • MSVC में 8 bytes से बड़े structs stack पर pass किए जाते हैं। यह ABI का ऐसा विवरण है जिस पर portable code में निर्भर नहीं होना चाहिए। लेकिन जो functions बार-बार call नहीं होते, उनके लिए ज़्यादा तनाव न लें; और जो छोटे functions बार-बार call होते हैं, उनमें compiler को code inline करने देना चाहिए ताकि registers में arguments pass करने से भी अधिक उपयोगी optimizations सक्षम हो सकें।
  • Windows में default cdecl calling convention का उपयोग करते समय 8 bytes से बड़े structs registers में pass नहीं किए जाते।
  • amd64 में sysv amd64 ABI का उपयोग करते हुए 16 bytes से बड़े structs को by value pass और return करना धीमा है, लेकिन code को स्पष्ट बनाने के लिए यह अक्सर उचित होता है। बेशक, इस मामले में ऐसा लागू नहीं होता, लेकिन उदाहरण के लिए हर C++ compiler, Golang, OCaml, SBCL की तरह अपनी भाषा के भीतर custom ABI का उपयोग किया जा सकता है।
  • C++ में एक सामान्य नियम है कि non-primitive types को, जब तक कोई अच्छा कारण न हो, reference से (या ज़रूरत हो तो pointer से) pass करना चाहिए। यह ABI के कारण भी है और copy या move constructors से बचने के लिए भी। यदि performance optimization चाहिए, तो C++ में यह उन उबाऊ low-level details में से एक है जिन पर ध्यान देना पड़ता है।
  • लेख एक बहुत ही विशेष benchmark का लिंक देता है, जिसमें Java (JIT) C++ से तेज़ है और यहाँ तक कि Scala से भी तेज़ है। इससे यह सवाल उठता है कि Julia HO क्या है और वह इतनी तेज़ क्यों है, Python और Pypy के बीच गति का अंतर इतना बड़ा क्यों है, Pypy का उपयोग न करने के कारण क्या हैं, और क्या उसे standard बन जाना चाहिए।
  • दिए गए उदाहरण में, caller को प्रभावित किए बिना struct Vector parameter type को const struct Vector & reference के रूप में pass करने के लिए बदला जा सकता है। pointer bugs वाले बहुत से C++ code में pointers का अनावश्यक उपयोग किया गया था, जबकि reference से pass करना आसान और अधिक सुरक्षित हो सकता था।