10 पॉइंट द्वारा GN⁺ 2025-08-05 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • V8 इंजन में JSON.stringify फ़ंक्शन की परफ़ॉर्मेंस को दो गुना से अधिक बढ़ाया गया, जिससे डेटा serialization की गति बेहतर हुई
  • side-effect रहित objects के लिए एक optimization path जोड़ा गया, जिससे कई defensive check लॉजिक छोड़े जा सके और सामान्य data objects में बड़ा speedup मिला
  • string processing के दौरान 1-byte/2-byte विभाजन, SIMD का उपयोग, और temporary buffer structure में बदलाव जैसे hardware और memory स्तर के उन्नत तरीकों को लागू किया गया
  • number conversion प्रक्रिया में मौजूदा Grisu3 algorithm को Dragonbox से बदल दिया गया, जिससे Number.toString() कॉल्स में भी तेज़ conversion संभव हुआ
  • कुछ arguments और object shapes में यह फिर सामान्य serialization path पर लौटता है, लेकिन ज़्यादातर web development स्थितियों में optimization का लाभ अपने-आप मिलता है

अवलोकन

  • JSON.stringify JavaScript में data को string में बदलने वाला एक core function है
  • इस फ़ंक्शन की performance में सुधार का सकारात्मक असर network requests या localStorage save जैसी web की बहुत महत्वपूर्ण operations पर भी पड़ता है
  • नवीनतम V8 engineering के ज़रिए इस feature की speed दो गुना से अधिक बेहतर हुई है, और इसके प्रमुख optimization तरीकों का विस्तार से परिचय दिया गया है

side-effect रहित Fast Path

  • optimization का मुख्य बिंदु यह है कि side-effect न होने वाली स्थितियों में ही इस्तेमाल होने वाला तेज़ serialization path लागू किया गया
  • ऐसी स्थितियों में object traversal recursion की जगह iterative structure से किया जाता है, जिससे stack overflow check की ज़रूरत नहीं पड़ती और अधिक गहरे objects को serialize करना भी संभव होता है
  • जब data object सरल होता है, तो V8 धीमे सामान्य logic की जगह इस Fast Path का उपयोग करके कई checks छोड़ देता है और speed बढ़ाता है

विभिन्न string representations का प्रबंधन

  • V8 1-byte/2-byte characters (ASCII/non-ASCII) के आधार पर strings को अलग तरीके से store करता है, और यदि एक भी non-ASCII character हो तो पूरी string को 2-byte रूप में संभाला जाता है
  • string serialization performance के लिए हर string type के लिए अलग algorithm version compile किया जाता है
  • processing के दौरान string instance type की जाँच करनी होती है, इसलिए यदि 2-byte string मिलती है तो उपयुक्त 2-byte serializer state को संभाल लेता है
  • इसकी वजह से string encoding के अनुसार path switch करने का overhead लगभग नहीं के बराबर है
  • परिणाम के लिए 1-byte और 2-byte buffers अलग-अलग बनाए जाते हैं और अंत में बस उन्हें merge कर दिया जाता है

SIMD से string serialization optimization

  • JavaScript strings में ऐसे characters हो सकते हैं जिन्हें JSON serialization के समय escape करना पड़ता है
  • लंबी strings को SIMD hardware instructions (जैसे ARM64 Neon) से एक बार में कई bytes जाँचकर process किया जाता है
  • छोटी strings के लिए SWAR तरीका इस्तेमाल होता है, जिसमें general-purpose registers में bit operations के ज़रिए कई characters को एक साथ process किया जाता है
  • किसी भी तरीके में, ज़्यादातर मामलों में बिना अतिरिक्त बदलाव के पूरी string को तेज़ी से copy किया जा सकता है

Express Lane जोड़ा गया

  • Fast Path के भीतर भी property checks जैसी बार-बार होने वाली प्रक्रियाओं के बिना केवल keys copy करके serialization संभव बनाने के लिए Express Lane जोड़ा गया
  • object के hidden class flag का उपयोग करके, यदि keys में Symbol न हो, वे सभी enumerable हों, और उन्हें escape की ज़रूरत न हो, तो उसे 'fast-json-iterable' के रूप में mark किया जाता है
  • उसी hidden class वाले दूसरे object को serialize करते समय अलग से checks किए बिना सीधे key copy की जाती है
  • इस तकनीक का उपयोग JSON.parse में भी तेज़ key comparison के लिए किया गया है

तेज़ double-to-string algorithm

  • numbers को strings में बदलने की प्रक्रिया भी उच्च आवृत्ति और जटिलता वाली होती है
  • मौजूदा Grisu3 algorithm को Dragonbox से बदलने पर Number.prototype.toString() की कुल calls में भी performance improvement मिला

temporary buffer structure optimization

  • string build करते समय पहले एक single contiguous buffer इस्तेमाल होता था, जिसमें space कम पड़ने पर हर बार पूरा data copy करने का overhead आता था
  • नया तरीका segmented buffer structure अपनाता है, जिसमें ज़रूरत के अनुसार कई छोटे buffers को जोड़ा जाता है
  • इससे space कम पड़ने पर पूरा data copy किए बिना सिर्फ नया buffer allocate करना पड़ता है

सीमाएँ

  • Fast Path केवल सरल data serialization पर लागू होता है
  • नीचे दी गई शर्तें पूरी न होने पर सामान्य path इस्तेमाल होता है
    • replacer या space argument का उपयोग नहीं होना चाहिए (Pretty-Print या transformation नहीं)
    • object एक सरल object होना चाहिए, जिसमें custom toJSON method न हो
    • index-based properties होने पर यह slow path पर चला जाता है
    • ConsString जैसे special strings को यह process नहीं करता
  • अधिकांश data serialization, API response generation, configuration caching जैसे सामान्य उपयोगों में optimization का लाभ अपने-आप लागू हो जाता है

निष्कर्ष

  • JSON.stringify की मूल design से लेकर memory handling और character processing तक पूरे क्षेत्र में approach को फिर से बनाकर JetStream2 benchmark के आधार पर 2x से अधिक speedup हासिल किया गया
  • यह सुधार V8 version 13.8 (Chrome 138) या उसके बाद में तुरंत अनुभव किया जा सकता है

1 टिप्पणियां

 
GN⁺ 2025-08-05
Hacker News राय
  • महसूस हुआ कि NodeJS में inter-process communication के लिए JSON encoding एक बड़ा bottleneck है

    • ज़्यादातर लोग आखिरकार event loop latency कम करने के लिए काम को दूसरे thread पर भेजने की कोशिश करते हैं, लेकिन तब पता चलता है कि main thread का CPU load अंततः 3 गुना हो जाता है
    • array को एक-एक करके stringify करने वाले उदाहरण भी बहुत देखे हैं, लगता है अंदरूनी तौर पर भी कुछ ऐसा ही किया जाता है
    • इच्छा है कि V8 टीम इस हिस्से को और मज़बूत करे
    • जिज्ञासा है कि क्या कुछ data sets के लिए बिना bail out के इसे संभाला जा सकता है, या CString handling की समस्या का क्या होगा, और faststr फीचर फिर लौटेगा या नहीं
    • पिछले साल जब पहली बार Node performance analysis किया, तब JSON.stringify Node service में performance को रोकने वाले सबसे बड़े कारणों में से एक था
      • dictionary key के लिए stringify का इस्तेमाल करना पड़ता है, और apollo/express पूरे response को एक बार में string में serialize करते हैं, stream नहीं करते
      • JVM या Go से आने वाले नज़रिए से देखें तो Node में यह हिस्सा काफ़ी amateurish लगा
    • Python में भी बिल्कुल यही समस्या है
      • लगता है कि common patterns के लिए high-level API के ऊपर efficient IPC primitives होना अच्छा रहेगा
    • इस राय से सहमत हूँ कि communication में JSON encoding एक बड़ा bottleneck है
      • सोचता हूँ कि दुनिया भर में communication में JSON processing से होने वाला compute overhead कितना बड़ा है, और अगर bytes को किसी fixed format या parsing-efficient तरीके (जैसे ASN.1) में सीधे भेजा जाए तो क्या वह बेहतर होगा
    • V8 टीम को इस हिस्से को और आक्रामक रूप से optimize करने के ख़िलाफ़ हूँ, और जिन developers को यह समस्या है उन्हें दूसरे tools देखने की सलाह दूँगा
      • मुझे नहीं लगता कि Node/V8 backend या high-performance compute problems के लिए ख़ास तौर पर उपयुक्त है
      • Javascript web use के हिसाब से बनी है और लंबे समय तक वैसी ही रहेगी, इसलिए V8 टीम को यह समस्या हल करने की ज़रूरत नहीं है
      • Typescript टीम भी Go की ओर गई, और languages के बीच automatic conversion भी संभव है
    • ऐसा लगभग सिर्फ़ एक ही बार हुआ जब Worker पर offload करने से serialization/deserialization समय से ज़्यादा बचत मिली हो
      • data जितना बड़ा होता है, उतना ही महँगा message passing parallelization के फ़ायदे के बराबर पहुँच जाता है
  • पिछले लगभग 10 सालों में floating-point number serialization performance कितनी सुधरी है, यह देखकर बहुत हैरानी हुई

    • IEEE floating-point values को decimal UTF-8 strings में बदलकर फिर वापस parse करना न सिर्फ़ धीमा है, बल्कि बहुत नाज़ुक भी है
      • binary और decimal में ठीक-ठीक represent किए जा सकने वाले values अलग होते हैं, इसलिए बहुत सूक्ष्म error आ सकती है
  • कहा गया है कि JSON.stringify में replacer या space argument हो तो fast path लागू नहीं होता

    • तो क्या JSON.stringify(data, null, 0) के साथ भी fast path मिल सकता है, या arguments का undefined होना ही ज़रूरी है
  • SWAR escaping algorithm[1] बहुत हद तक उस implementation जैसा है जो मैंने Folly JSON में किया था[2]

  • इस काम की उपयोगिता पर शक नहीं है, लेकिन यह जानने की उत्सुकता है कि असल V8 ecosystem में कौन-सी ठोस समस्याएँ या data थे जहाँ JSON.stringify ने runtime पर दबदबा बना लिया था

    • ज़रूरी नहीं कि execution time का हिस्सा पूरी तरह हावी ही हो, लेकिन जब इसे रोज़ सैकड़ों करोड़ pages पर चलाया जाता है, तो वैश्विक बिजली-बचत का असर काफ़ी बड़ा हो सकता है
  • मुझे लगता है कि v8 की performance की जितनी तारीफ़ होनी चाहिए उतनी नहीं होती, आजकल JS बेहद तेज़ हो चुकी है

    • सचमुच प्रभावित करने वाला है, मुझे यह “1 billion dollars हो तो कोई भी समस्या हल की जा सकती है” का अच्छा उदाहरण लगता है
      • आगे चलकर अच्छा होगा अगर JS “strict”, “stricter” जैसी दिशा में और विकसित हो, ताकि वह compile/JIT के लिए आसान और सरल language बने
    • दूसरी तरफ़, v8 को इतनी हद तक optimize कर दिया गया है कि दुनिया भर में शायद सिर्फ़ लगभग 100 लोग ही उसके अंदरूनी हिस्सों को सही से समझते हैं, और ज़्यादातर developers के मन में यही रहता होगा: “मेरा JS तेज़ क्यों नहीं है?”
  • जिज्ञासा है कि दूसरे ecosystems की तुलना में यह कितना प्रभावशाली है

    • 10 साल से ज़्यादा समय तक JSON serialization की है, लेकिन यह इतना तेज़ रहा कि शायद ही कभी इसकी चिंता करनी पड़ी
    • simdjson एक core पर प्रति सेकंड GB स्तर का throughput दे सकता है, और prefetching/branch prediction जैसी optimizations को देखते हुए मुझे लगता है कि ज़्यादातर real-world workloads में JSON serialization को लगभग नज़रअंदाज़ किया जा सकता है
    • JSON की सबसे बड़ी कमी IO overhead है; serializer कितना भी तेज़ हो, अगर हर बार 100MB blob को storage में लिखना पड़े तो उसका फ़ायदा नहीं
  • “No indexed properties on objects” — कहा गया है कि fast path सिर्फ़ सामान्य string-key objects के लिए optimize किया गया है, लेकिन अगर array-like indexed properties हों तो वह slow path पर लौट जाता है

    • इसकी वजह क्या है, यह जानना चाहता हूँ
    • क्या इसका मतलब यह है कि integer जैसे दिखने वाले keys वाले object JSON array की तरह serialize हो जाते हैं? ऐसा तो नहीं...?
  • segmented buffer वाला approach पसंद आया, पहले fast-json-stringify जैसी userland libraries के rope tricks ख़ुद लगाने पड़ते थे, अब native में होना काफ़ी बेहतर है

    • जानना चाहता हूँ कि bailout conditions (जैसे replacer, space, custom .toJSON()) कितनी बार मिलती हैं, और क्या ऐसे मामलों में यह सीधे slow path पर fallback हो जाता है
  • V8 शानदार है, लेकिन शायद JS की वजह से performance में यह LuaJIT या JVM से पीछे रह जाता है

    • JVM का warm-up time लंबा होता है, लेकिन उसके बावजूद वह JS से बेहतर है
    • वजह JS ही है, मुझे लगता है V8, luajit और JVM दोनों से कहीं ज़्यादा advanced है
      • Java में real-time constraints कम हैं (और compiler भी है), यही उसका फ़ायदा है
    • JS का बहुत-सा overhead उसकी dynamic nature से आता है
      • asm.js ने object shape बदलने जैसे dynamic व्यवहार को मना किया था, इसलिए बहुत-सी checks skip की जा सकती थीं
    • “JVM भी” वाले वाक्यांश पर आपत्ति है, JVM top-tier है