AMD64 पर 16 बाइट से बड़े structs न पास करें

AMD64 पर 16 बाइट से बड़े structs को value के रूप में पास करने से प्रदर्शन चुपचाप गिर सकता है, क्योंकि System V और MSVC x64 ABIs ऐसे arguments को registers के बजाय stack पर spill करते हैं। Commenters C, C++, Rust, Zig, और अन्य languages में pass-by-value बनाम pass-by-reference की प्राथमिकता पर बहस करते हैं, जहाँ साफ़ APIs और move semantics को hot paths और calling conventions की छिपी लागत के खिलाफ तौला जाता है। कई लोग नोट करते हैं कि ये overheads profilers में देखना कठिन है, whole-program optimization या custom internal ABIs का सुझाव देते हैं, और blanket rules के बजाय profiling और context पर ज़ोर देते हैं.

ABI और Calling-Convention की सीमाएँ

  • मुख्य समस्या SysV AMD64 ABI है: 16 बाइट से बड़े structs registers की बजाय memory के जरिए पास किए जाते हैं, जिससे hot code में छिपा हुआ overhead बढ़ सकता है।
  • MSVC/x64 पर cutoff और भी छोटा है (8 bytes)। अलग-अलग platforms पर thresholds अलग हैं, इसलिए यह एक ABI detail है, कोई सार्वभौमिक नियम नहीं।
  • कुछ लोगों का कहना है कि बड़े returns hidden “return pointer” arguments के जरिए कुशलता से संभाले जाते हैं, इसलिए return-value overhead अक्सर parameter passing जितनी समस्या नहीं होती।

C/C++ में Pass-by-Value बनाम Pass-by-Reference

  • बहुत-से C++ codebases nontrivial types को default रूप से pointer या reference से pass करते हैं, और view types (string_view, span, FunctionRef) आम exceptions हैं।
  • कुछ लोगों का तर्क है कि pass-by-value अक्सर ठीक है, या तब बेहतर भी हो सकता है जब इससे move semantics और सरल APIs मिलती हैं, खासकर std::string जैसे types के लिए।
  • दूसरे लोग बताते हैं कि const references भी बड़े structs के लिए ABI-निर्धारित stack spills से नहीं बचाते; केवल अलग-अलग scalar parameters में तोड़ने से ही सब कुछ registers में रह सकता है।
  • T&& बनाम T by value के उपयोग पर बहस है; कुछ लोग non-template T&& को pass-by-value plus move की तुलना में code smell मानते हैं।

Language-Specific Notes

  • Zig structs के लिए transparently pass-by-value या pass-by-reference चुन सकता है, लेकिन इससे confusing bugs हुए हैं; इसे कम करने के लिए proposals (जैसे noalias-by-default) दिए गए हैं।
  • Rust का internal ABI SysV से स्वतंत्र है; FFI वह जगह है जहाँ ये size issues मायने रखते हैं। Borrowed types (&str, slices) स्वाभाविक रूप से “fat pointers” हैं और पास करने में सस्ते हैं।
  • .NET developers भी बड़े structs पास करने को लेकर चिंतित रहते हैं; implied guidance यह है कि कुछ references से बड़े structs से बचें।

Performance Tradeoffs और Profiling

  • 24-byte object को copy करने और pointer से pass करने के बीच हमेशा साफ़ तौर पर बेहतर विकल्प नहीं होता; इसमें extra copies और cache locality बनाम pointer chasing और संभावित cache misses का tradeoff होता है।
  • कुछ participants ज़ोर देते हैं कि ऐसे optimizations micro-level हैं और सामान्यतः profiling तथा whole-program optimization (LTO) के बाद ही अपनाने चाहिए, लेकिन दूसरे कहते हैं कि मौजूदा profilers calling-convention overhead को स्पष्ट रूप से दिखाने में कमजोर हैं।
  • एक वास्तविक benchmark में बड़े-by-value parameters से बचने पर लगभग 2× speedup (large ranking jump) देखा गया, जिससे लगता है कि tight loops में यह महत्वपूर्ण हो सकता है।