1 पॉइंट द्वारा GN⁺ 2025-05-23 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • Rust में लिखा गया rav1d AV1 डिकोडर, C-आधारित dav1d की तुलना में लगभग 9% धीमा पाया गया
  • बफ़र initialization optimization और struct comparison logic में सुधार से क्रमशः 1.5% और 0.7% की स्पीड बढ़ोतरी देखी गई
  • profiling tool samply का उपयोग करके दोनों versions के performance gap के कारणों को विस्तार से पहचाना गया
  • Rust के डिफ़ॉल्ट PartialEq implementation की जगह byte-level comparison का उपयोग करके efficiency बढ़ाई गई
  • इस optimization से कुल performance gap का लगभग 30% सुधरा, लेकिन अभी भी optimization की गुंजाइश बाकी है

पृष्ठभूमि और अप्रोच विधि

  • rav1d, dav1d AV1 डिकोडर को c2rust के जरिए Rust में port करके, asm optimized functions और Rust-विशिष्ट safety improvements को शामिल करने वाला प्रोजेक्ट है
  • सार्वजनिक रूप से एक baseline performance target तय है, और Rust-आधारित rav1d, C-आधारित dav1d से लगभग 5% धीमा है
  • जटिल वीडियो डिकोडर की समग्र संरचना के बजाय, समान input पर binary runtime के अंतर पर ध्यान केंद्रित करके विश्लेषण किया गया
  • performance measurement tool (hyperfine) और profiler (samply) से व्यवस्थित तुलना की गई
  • target environment macOS M3 chip था, और single-thread execution से इसे सरल बनाया गया

performance measurement: default comparison

  • एक ही test file (Chimera-AV1-8bit-1920x1080-6736kbps.ivf) पर अलग-अलग build और benchmark चलाए गए
  • rav1d: लगभग 73.9 सेकंड, dav1d: लगभग 67.9 सेकंड, यानी लगभग 6 सेकंड (9%) का runtime अंतर पाया गया
  • दोनों compilers (Clang, Rustc) लगभग समान LLVM version का उपयोग करते हैं

profiling analysis

  • samply profiler से प्रत्येक executable के function-level sample counts की तुलना की गई
  • NEON (ARM SIMD) आधारित assembly functions के call paths और sample distribution पर विशेष ध्यान दिया गया
  • dav1d, asm functions को branch-call करने के लिए अलग filter functions का उपयोग करता है, जबकि rav1d एक dispatch function में सब कुछ संभालता है
  • cdef_filter_neon_erased function के Self samples, dav1d के दो functions के कुल sample count से लगभग 270 अधिक निकले (कुल का 1%)
  • विश्लेषण में यह सामने आया कि temporary buffer (zero-initialized buffer) अनावश्यक रूप से बहुत बड़ा initialize किया जा रहा था

buffer initialization removal optimization

  • Rust, safety के लिए [0u16; LEN] जैसी शैली से अपने-आप zeroing करता है
  • लेकिन C (dav1d) बफ़र को स्पष्ट रूप से zeroing नहीं करता, और वास्तव में उपयोग होने वाले हिस्सों में ही value लिखता है
  • Rust में std::mem::MaybeUninit का उपयोग करके अनावश्यक initialization cost हटाई गई
  • cdef_filter_neon_erased function के Self samples 670 से घटकर 274 रह गए
  • एक अन्य बड़े Align16 buffer के लिए भी initialization को loop के बाहर hoist करके cost को एक बार तक सीमित किया गया
  • optimization के बाद benchmark लगभग 72.6 सेकंड रहा, यानी 1.2 सेकंड (1.5%) का सुधार हुआ

struct comparison optimization

  • profiling के inverted stack analysis में पता चला कि add_temporal_candidate function अपेक्षा से कम efficient तरीके से काम कर रहा था
  • इस function में Mv struct के field comparison (PartialEq auto implementation) से अनावश्यक रूप से धीमा code बन रहा था
  • C में union का उपयोग करके uint32_t स्तर पर efficient comparison किया जाता है
  • Rust में unsafe से बचते हुए zerocopy::AsBytes trait के साथ byte slice स्तर पर comparison लागू किया गया
  • इस optimization से फिर 0.5 सेकंड (लगभग 0.7%) का performance improvement मिला

परिणाम और निष्कर्ष

  • दो सरल optimizations (buffer initialization हटाना, struct byte comparison) से runtime में 2% से अधिक की कमी हासिल हुई
  • अभी भी लगभग 6% का performance gap बाकी है, और आगे optimization की गुंजाइश काफ़ी है
  • profiler snapshots के बीच तुलना करने की विधि प्रभावी साबित हुई
  • rav1d और dav1d के snapshot analysis के आधार पर आगे और optimization की संभावना अधिक है
  • project maintainers के सक्रिय feedback और सहयोग से safety से समझौता किए बिना सुधार संभव हुआ

सारांश

  • profiler (samply) और benchmark (hyperfine) tools की मदद से rav1d और dav1d के बीच 6 सेकंड (9%) के runtime gap का सटीक विश्लेषण किया गया
  • दो मुख्य optimizations:
    • ARM-विशिष्ट code में अनावश्यक buffer zeroing हटाना (1.2 सेकंड, -1.6%)
    • छोटे numeric struct की PartialEq implementation को तेज byte comparison में बदलना (0.5 सेकंड, -0.7%)
  • नया unsafe code जोड़े बिना, हर optimization कुछ दर्जन lines के भीतर संक्षिप्त रहा
  • maintainers के सहयोग और PR review के जरिए reliability और quality improvement दोनों हासिल किए गए
  • अभी भी लगभग 6% का performance gap बचा है, इसलिए आगे profiler-आधारित comparative optimization research की काफी संभावना है

आगे बढ़िए और इसे आज़माइए! शायद rav1d कभी dav1d से भी तेज़ हो जाए 👀🦀.

1 टिप्पणियां

 
GN⁺ 2025-05-23
Hacker News टिप्पणियाँ
  • यह राय साझा की गई कि दो u16 की तुलना वाला issue एक दिलचस्प विषय है, और संबंधित issue का लिंक दिया गया https://github.com/rust-lang/rust/issues/140167
    • यह आश्चर्य जताया गया कि चर्चा में store forwarding का ज़िक्र नहीं आया। -O3 में code generation का नतीजा कुछ ज़्यादा है, लेकिन -O2 में यह उचित निर्णय लगता है। अगर किसी struct में से एक पर ऑपरेशन अभी-अभी हुआ हो, तो 32-bit load की कोशिश में store forwarding fail हो सकता है, जिससे performance gain बेअसर हो सकता है। non-inline/non-PGO स्थिति में compiler के पास optimization की उपयुक्तता तय करने के लिए ज़रूरी जानकारी कम होती है, यह बात भी उठाई गई
    • यह अच्छा लगा कि issue discussion सिर्फ़ “मेरे साथ भी हुआ” या “यह कब ठीक होगा” जैसी टिप्पणियों से नहीं भरी हुई है। एक web developer के रूप में GitHub issues से असंतोष भी ईमानदारी से साझा किया गया
    • यह राय दी गई कि यह मामला दिखाता है कि compiler development कितना जटिल काम है, और भरोसा जताया गया कि C-family compilers भी ऐसे issues को इससे बेहतर नहीं संभाल पाएँगे
  • यह जिज्ञासा जताई गई कि blog post में profiler results कैसे embed किए गए, और पूछा गया कि क्या HTML nodes को ज्यों का त्यों copy किया गया था
  • यह दिलचस्प लगा कि buffer initialization (zeroing) छोड़ने के performance benefit पर यह लेख कुछ दिन पहले आए संबंधित लेख के बाद प्रकाशित हुआ, और पुराने लेख का लिंक साझा किया गया https://news.ycombinator.com/item?id=44032680
  • यह कहा गया कि मुख्य लेख का शीर्षक वास्तविक उपलब्धि की तुलना में बहुत ज़्यादा संकोची है; असल में दो अच्छे optimizations की वजह से 2.3% speedup मिला है
    • यह राय दी गई कि 1.5% improvement सिर्फ़ aarch64 पर लागू होती है, इसलिए उसे overall figure की तरह बताना थोड़ा अनुचित है। arm/x86 अनुपात को देखते हुए उसे लगभग आधा मानना चाहिए
  • पोस्ट को उपयोगी बताया गया और 16-bit integer pair comparison में inefficient code का मिलना प्रभावशाली लगा
    • यह जिज्ञासा जताई गई कि क्या Rust/LLVM developers इस optimization को जहाँ संभव हो अपने-आप लागू कर सकते हैं। यह भी कहा गया कि Rust में memory initialization से जुड़ी जानकारी कहीं अधिक सटीक होती है
  • यह विचार रखा गया कि अगर बाकी सब शर्तें समान हों, तो ऐसे codec को Rust की बजाय WUFFS जैसी भाषा या उसके समकक्ष किसी special-purpose language में संभालना चाहिए। यह अनुभव भी साझा किया गया कि dav1d जैसे जटिल code को WUFFS में बदलना मौजूदा C code के translation/clean up से कहीं ज़्यादा कठिन है। फिर भी ऐसे प्रयास को मूल्यवान और सभ्यता-स्तर पर निवेश करने योग्य बताया गया
    • यह समझाया गया कि WUFFS, Matroska, webm, mp4 जैसे container parsing के लिए तो उपयुक्त है, लेकिन video decoder के लिए बिल्कुल नहीं। उसमें dynamic memory allocation नहीं है, इसलिए dynamic data को संभालना चुनौतीपूर्ण है। ज़ोर दिया गया कि video codec सिर्फ़ file parsing नहीं करते, बल्कि बहुत विविध dynamic state management की ज़रूरत होती है
  • rav1d bounty की प्रगति जानने की उत्सुकता में यूँ ही सवाल किया जा रहा था, और यह सहमति जताई गई कि इसी तरह सोचने वाला कोई और भी है
  • यह कहा गया कि मज़ेदार meme से शुरू होने वाला लेख आमतौर पर अच्छी posting का संकेत होता है। हाल की चर्चित “Rav1d AV1 Decoder Rust optimization $20k bounty” चर्चा से इसका संबंध भी बताया गया, और संबंधित लिंक जोड़ा गया https://news.ycombinator.com/item?id=43982238
    • इस मामले को मज़ाकिया अंदाज़ में ‘Nominative determinism’ का साफ़ उदाहरण बताया गया
  • ईमानदारी से कहा गया कि पहला optimization थोड़ा surprising था, क्योंकि वह ऐसा आम pattern है जिसे perf अच्छे से इस्तेमाल करके आसानी से ढूँढा जा सकता है। zeroing issue के बारे में लगा कि शायद उस पर पहले पोस्ट में ही चर्चा हो चुकी होगी। दूसरा optimization ज़्यादा जटिल और दिलचस्प था, लेकिन वह भी perf द्वारा दिखाई गई दिशा से ही मिला। सलाह दी गई कि perf tool की उपयोगिता को कम न आँकें
    • यह स्पष्ट किया गया कि केवल perf नहीं, बल्कि C version और Rust version के बीच differential profiling और manual matching की प्रक्रिया से यह पता चला। यह भी कहा गया कि perf diff feature है, लेकिन symbol names अलग होने के कारण automatic matching मुश्किल हो जाती है
    • यह भी बताया गया कि दृष्टिकोण aarch64-आधारित Apple devices के नज़रिए से था। अनुभव के आधार पर कहा गया कि अलग background वाले लोग बाद में “बिल्कुल obvious” लगने वाली चीज़ों को भी जल्दी पकड़ सकते हैं
  • यह अटकल लगाई गई कि हाल में ffmpeg के Twitter account को Rust-संबंधित issue पर आधिकारिक रुख़ व्यक्त करना पड़ा, और उसके पीछे यही issue हो सकता है। संबंधित tweet का लिंक साझा किया गया https://x.com/ffmpeg/status/1924137645988356437?s=46
    • ffmpeg के Twitter account को पढ़कर ffmpeg के उपयोग को लेकर संदेह पैदा होने की ईमानदार भावना साझा की गई। यह अफ़सोस भी जताया गया कि कोई अच्छा विकल्प नहीं है। developer community को बहुत toxic बताया गया। यह कहा गया कि maximum performance महत्वपूर्ण हो सकती है, लेकिन बाहरी दुनिया से data लेने-देने वाले माहौल में ffmpeg में हर साल कई remote vulnerabilities (CVE) सामने आ सकती हैं। security के लिहाज़ से कड़े sandbox की ज़रूरत पर ज़ोर दिया गया, और यह राय रखी गई कि तेज़ और सुरक्षित समाधान साथ लेकर चलने वाला कोई बीच का रास्ता होना चाहिए। संबंधित लिंक साझा किया गया https://ffmpeg.org/security.html
    • यह सुझाव दिया गया कि बेहतर प्रतिक्रिया dav1d की performance सुधारकर देना होगा। खेल के रिकॉर्ड की तुलना करते हुए कहा गया कि सिर्फ़ रिकॉर्ड थोड़ा बेहतर कर लेने से उतना असर नहीं होता जितना असली नया रिकॉर्ड बनाने से होता है। मज़ाकिया ढंग से कहा गया कि असली समाधान वही है जो व्यावहारिक रूप से तेज़ और वास्तव में नया हो