जब compiler आपको चौंका दे
(xania.org)- एक साधारण integer summation function को compile करते समय GCC और Clang की optimization में अंतर देखने का मामला
- GCC loop के भीतर एक बार में दो संख्याएँ जोड़ने वाली
x*2 + 1रूप की loop optimization करता है - Clang loop को पूरी तरह हटा देता है और गणना को
v(v - 1)/2के closed-form formula से बदल देता है - इस रूपांतरण से कोड O(n) से O(1) में बदल जाता है, और गणितीय simplification अपने-आप हो जाती है
- 20 साल से अधिक समय से compiler के साथ काम कर रहे लेखक को भी चौंका देने वाला यह उदाहरण, compiler की intelligent optimization क्षमता दिखाता है
GCC की loop optimization
- जब एक साधारण integer summation function लिखा गया, तो GCC ने loop-आधारित efficient summation code generate किया
- loop के भीतर
lea edx, [rdx+1+rax*2]instruction का उपयोग करके दो संख्याएँ एक साथ जोड़ी गईं - यह
xऔरx+1को जोड़ने की operation कोx*2 + 1में बदलने का रूप है
- loop के भीतर
- optimization level को
-O3तक बढ़ाने पर parallel addition (vectorization) के जरिए loop को और तेज़ी से process किया जाता है - यह तरीका loop को बनाए रखते हुए भी operation efficiency को अधिकतम करने वाली पारंपरिक optimization का रूप है
Clang की गणितीय optimization
- वही कोड Clang से compile करने पर loop पूरी तरह गायब हो जाता है
- Clang input value के positive होने की जाँच करने के बाद
lea,imul,shrinstructions की एक श्रृंखला से गणना करता है - नतीजतन, इसे
(v² - v)/2, यानी integer sum के closed-form formula में बदला जाता है
- Clang input value के positive होने की जाँच करने के बाद
- यह रूपांतरण loop को हटाकर constant-time (O(1)) calculation से बदल देता है
- लेखक ने इस परिणाम को “integer sum का गणितीय हल compiler ने खुद खोज लिया” जैसा बताया
सूत्र के विस्तार की प्रक्रिया
- Clang द्वारा generate किए गए assembly code को गणितीय रूप से उल्टा समझने पर निम्नलिखित रूपांतरण दिखाई देता है
v + ((v - 1)(v - 2) / 2) - 1- इसे विस्तार और सरलीकरण करने पर यह
(v² - v)/2बन जाता है
- अंततः यह
v(v - 1)/2के रूप में आता है, जो 1 से v तक के योग के सूत्र से मेल खाता है - इसे compiler द्वारा गणितीय pattern पहचानकर की गई optimization के उदाहरण के रूप में प्रस्तुत किया गया है
compiler का intelligent व्यवहार
- Clang इस विशेष instruction sequence का उपयोग क्यों करता है, इसे overflow से बचाव और induction variable tracking के तरीके से समझाया गया है
- सटीक internal behavior का कारण पूरी तरह स्पष्ट नहीं है, लेकिन इसे Clang की internal optimization logic के संयुक्त प्रभाव का परिणाम बताया गया है
- लेखक ने इस नतीजे को “विनम्र बनाने वाला और प्रेरक अनुभव” कहा
समापन और series का संदर्भ
- यह लेख ‘Advent of Compiler Optimisations 2025’ series का 24वाँ लेख है
- इसमें यह देखा गया है कि compiler code transformation के जरिए गणितीय simplification और performance improvement कैसे हासिल करता है
- लेखक कहते हैं, “compiler आज भी मुझे चौंकाते हैं,” और लंबे अनुभव के बावजूद बने रहने वाले तकनीकी विस्मय पर ज़ोर देते हैं
1 टिप्पणियां
Hacker News की राय
इस feature को implement करने वाला code ScalarEvolution.cpp और IndVarSimplify.cpp में है
इस तरह की optimization वाकई दिलचस्प है
compiler optimization को मोटे तौर पर दो हिस्सों में बाँटा जा सकता है — data flow analysis और pattern recognition तथा replacement
पहला ज़्यादातर programs में असरदार होता है, और दूसरा धीरे-धीरे कम return देने वाले patterns को जमा करने का काम है
linked example समझदार और मज़ेदार है, लेकिन असल में इसकी बहुत ज़्यादा value नहीं है। 45 साल में मैंने ऐसा loop कभी नहीं लिखा
बेशक, इस तरह के pattern substitution high-level code से अपने-आप generate होते हैं, इसलिए ये अब भी महत्वपूर्ण हैं
सच कहूँ तो, शायद मैं थोड़ा इसलिए बड़बड़ा रहा हूँ क्योंकि मेरा optimizer ऐसी optimization नहीं कर पाता
यह Scalar Evolution(SCEV) नाम की feature है, और LLVM में यह काफ़ी जटिल तरीके से implement की गई है
इसी तरह की optimization का एक और उदाहरण “Do not optimize away” लेख में है
इस optimization की असली शानदार बात यह है कि यह generic है
यह सिर्फ “finite integer sequence का sum” pattern पहचानने तक सीमित नहीं है, बल्कि सामान्य रूप से लागू होती है — यही प्रभावशाली है
लगता है compiler में और ज़्यादा closed form जोड़े जा सकते हैं। पता नहीं यह इसके लायक होगा या नहीं
इससे जुड़ी एक अवधारणा Wilf–Zeilberger pair है
एक बार फिर GCC, Clang से धीमा निकला, यह देखकर हैरानी हुई
GCC को 20 साल की बढ़त मिली थी, फिर भी अब भी ऐसे मामले हैं जहाँ Clang ज़्यादा तेज़ code निकालता है
पहले bit extraction करते समय Clang ने सिर्फ दो shifts किए थे, जबकि GCC ने तीन
सोच रहा हूँ क्या किसी ने graph coloring problem को constant से replace करने वाली optimization आज़माई है
यह implementation और specification की सीमा को धुंधला करने वाला उदाहरण है
हम सोचते हैं कि हम implementation लिख रहे हैं, लेकिन असल में हम specification के proxy लिख रहे होते हैं
यानी compiler imperative machine के illusion का निर्माण करता है
शुरुआत में यह देखकर हैरानी हुई कि Matt को इस तरह के behavior के बारे में पता नहीं था
मैंने भी कॉलेज के दिनों में LLVM IR के साथ खेलते हुए recursion का multiplication में simplify हो जाना देखा था, और चौंक गया था
अगर Matt को यह नहीं पता था, तो शायद इसका मतलब है कि यह optimization सिर्फ उन सरल cases पर लागू होती है जिनसे उसका वास्ता नहीं पड़ता
YouTube वीडियो लिंक