3 पॉइंट द्वारा GN⁺ 2025-03-11 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • CPython प्रोजेक्ट ने हाल ही में bytecode इंटरप्रेटर के लिए एक नई implementation strategy अपनाई है। शुरुआती नतीजों में अलग-अलग platforms पर औसतन 10-15% प्रदर्शन सुधार दिखा।
  • हालांकि, यह प्रदर्शन सुधार मुख्य रूप से LLVM 19 के regression को bypass करने का परिणाम था। बेहतर baselines (जैसे GCC, clang-18, या खास tuning flags वाले LLVM 19) से तुलना करने पर प्रदर्शन सुधार घटकर 1-5% रह गया।

प्रदर्शन परिणाम

  • कई compilers और configuration options का उपयोग करके CPython इंटरप्रेटर के कई builds का benchmark किया गया। परीक्षण Intel server और Apple M1 Macbook Air पर किए गए।
  • सभी builds में LTO और PGO का उपयोग किया गया। clang18 को baseline मानते हुए pypeformance/pyperf compare_to में reported averages का उपयोग किया गया।
  • Compiler performance comparison
    • Apple M1 Macbook Air :
      • clang18: baseline
      • clang19: 1.12x धीमा
      • clang19.taildup: 1.02x धीमा
      • clang19.tc: 1.00x धीमा
      • gcc: N/A
  • tail-call इंटरप्रेटर ने फिर भी clang-18 की तुलना में speedup दिखाया, लेकिन clang-19 पर जाने के साथ slowdown कहीं अधिक नाटकीय था।

LLVM regression

संक्षिप्त पृष्ठभूमि

  • पारंपरिक bytecode इंटरप्रेटर while loop के भीतर switch statement से बना होता है। अधिकांश compilers switch को jump table में compile करते हैं।
  • आधुनिक C compilers label के address लेने और उन्हें "computed goto" के रूप में उपयोग करने वाले pattern को support करते हैं। CPython ने tail-call काम से पहले तक इसी pattern का उपयोग किया था।

LLVM 19 regression

  • LLVM 19 ने tail-duplication pass पर सीमाएं लगा दीं, ताकि IR size किसी निश्चित सीमा से अधिक होने पर duplication रुक जाए। इसके कारण CPython में सभी dispatch jumps merge हो गए, और computed goto-based implementation का उद्देश्य पूरी तरह निष्फल हो गया।

अतिरिक्त असामान्यताएं

  • यह तो स्पष्ट है कि tail-call duplication logic में बदलाव ने regression पैदा किया, लेकिन regression के परिमाण को पूरी तरह समझाया नहीं जा सकता।
  • आधुनिक processors पर 2-4% का speedup अधिक सामान्य है।

क्या computed goto ज़रूरी है?

  • clang19.nocg benchmark दावा करता है कि वह clang19 से तेज़ है। यह दिखाता है कि compiler switch-based इंटरप्रेटर का उपयोग करके वही optimization कर सकता है।

सुधार

  • LLVM pull request 114990 ने regression को ठीक किया। इस fix ने अपेक्षित performance को बहाल कर दिया।

विचार

benchmarking के बारे में

  • सिस्टम optimization करते समय benchmarks और benchmarking methodology तैयार की जाती है, और प्रस्तावित बदलावों का मूल्यांकन किया जाता है।
  • benchmarks को किसी एक data point को सामान्यीकृत करने के लिए अधिक assumptions और विश्वास की आवश्यकता होती है।

baseline

  • जब कोई नया solution या method प्रस्तावित किया जाता है, तो उसकी तुलना "मौजूदा सबसे अच्छी ज्ञात approach" से करना सामान्य बात है।

software engineering के बारे में

  • software systems जटिल, परस्पर जुड़े हुए और तेज़ी से बदलने वाले हैं।
  • optimizing compilers पर programmer के इरादे का सम्मान करते हुए code को optimize करने का दबाव रहता है।

optimizing compilers

  • musttail attribute optimization से जुड़े compiler features की एक नई श्रेणी को दर्शाता है। यह performance-sensitive code लिखने के लिए अधिक शक्तिशाली शैली दे सकता है।

nix के बारे में एक और बात

  • nix इस प्रोजेक्ट में बहुत उपयोगी रहा। इसने Python इंटरप्रेटर के कई versions को manage और build करने में बड़ी मदद की।

1 टिप्पणियां

 
GN⁺ 2025-03-11
Hacker News टिप्पणियाँ
  • नमस्ते। मैं वही व्यक्ति हूँ जिसने CPython में tail-calling interpreter जोड़ने वाला PR लिखा था

    • सबसे पहले, इस समस्या की जड़ तक पहुँचने के लिए लगभग एक महीना खर्च करने वाले Nelson का धन्यवाद करना चाहूँगा
    • साथ ही, इतनी बड़ी गलती करने पर मुझे बहुत शर्मिंदगी और खेद है
    • CPython टीम ने भी यह उम्मीद नहीं की थी कि हमारे इस्तेमाल किए गए compiler में ऐसा bug होगा
    • मैंने माफ़ीनामा ब्लॉग पोस्ट यहाँ प्रकाशित किया है: लिंक
  • benchmarking सच में बहुत कठिन काम है

    • हाल ही में मैंने algorithm को लगभग 15% तेज़ बनाने का एक तरीका खोजा
    • लेकिन testing के दौरान, तेज़ version वाले function को call किए बिना भी original code 15% तेज़ हो गया
    • यह code और memory layout की समस्या थी, क्योंकि CPU cache के साथ alignment बेहतर हो गया था
    • Casey Muratori इस तरह के विषय पर एक दिलचस्प series चला रहे हैं
  • लेखक ने इस समस्या की सच्चाई सामने लाई, इसके लिए प्रशंसा बनती है

    • Python 3.14 का tail-call interpreter अब भी एक अच्छा सुधार है
    • इस घटना ने benchmarking में सख़्ती और अलग-अलग environments में testing के महत्व को सिखाया
    • और अब एक compiler bug भी सामने आया है, जिससे सभी को फ़ायदा हो सकता है
    • सोचता हूँ कि कितने "X% faster" नतीजे वास्तव में benchmarking artifact या किसी अज्ञात regression की वजह से होते हैं
  • यह इस बात का अच्छा उदाहरण है कि C कोई "machine के क़रीब" language नहीं है

    • clang-19 computed goto interpreter को "सही तरीके से" compile करता है, लेकिन optimization के इरादे से बिल्कुल अलग output बनाता है
    • compiler के दूसरे versions भी "भोले" switch() आधारित interpreter पर optimization लागू करते हैं
  • compiler loops को जिस तरह व्यवस्थित करता है, उसके कारण tail-call interpreter उतना प्रभावी नहीं है जितना बताया गया था

    • CPU architecture और version बहुत मायने रखते हैं
    • C abstract machine इरादे को सही तरह व्यक्त करने के लिए काफ़ी low-level नहीं है
    • कुछ बेहद paranoid interpreter implementations फिर सीधे assembly लिखने पर लौट आती हैं
    • luajit एक macro system लागू करता है, जिससे efficient assembly loop implementations को अलग-अलग architectures के बीच portable बनाया जा सके
  • Python build की performance का मूल्यांकन करना बहुत कठिन है

    • हाल ही में astral टीम ने दिखाया कि conda-forge build, ज़्यादातर दूसरे builds से तेज़ है
    • दिलचस्प होगा देखना कि tail-call interpreter दूसरे build optimizations के साथ कैसे काम करता है
  • संबंधित चर्चा:

  • शानदार लेख है

    • जिन referenced articles में से एक में कहा गया है कि 3.14.0a5, 3.13 से 1.12 गुना तेज़ है
    • यह उलझन है कि क्या benchmark किसी दूसरे process के overload होने की स्थिति में चलाया गया था
    • benchmark को बाहरी variables हटाने के लिए सख़्ती से नियंत्रित environment में चलाया जाना चाहिए
  • हाल ही में मैंने Python 3.9 से 3.13 तक benchmarking की

    • 3.11 तक performance बेहतर हुई थी, लेकिन 3.12 और 3.13, 3.11 से लगभग 10% धीमे थे
    • मुझे लगा कि शायद मेरा अपना benchmark काफ़ी नहीं है, लेकिन core service में deploy करने पर भी वही बदलाव देखा
  • सोच रहा हूँ कि इस तरह का optimization tail-call optimization से कैसे संबंधित है

    • interpreter jump table implementation का stack frame creation पर असर नहीं पड़ना चाहिए