2024 में 10 लाख समवर्ती टास्क चलाने के लिए आवश्यक मेमोरी क्षमता
(hez2010.github.io)बेंचमार्क
-
Coroutine क्या है?
- Coroutine कंप्यूटर प्रोग्राम का एक ऐसा घटक है जो प्रोग्राम के execution को अस्थायी रूप से रोककर फिर से शुरू कर सकता है, और यह cooperative multitasking के लिए subroutine का एक सामान्यीकृत रूप है.
- यह cooperative tasks, exceptions, event loop, iterators, infinite lists और pipes जैसे प्रोग्राम घटकों को लागू करने के लिए उपयुक्त है.
-
Rust
- दो प्रोग्राम लिखे गए:
tokioऔरasync_stdका उपयोग करने वाले प्रोग्राम. - दोनों ही Rust में आमतौर पर उपयोग किए जाने वाले asynchronous runtime हैं.
- दो प्रोग्राम लिखे गए:
-
C#
- C# भी Rust की तरह
async/awaitको सपोर्ट करता है. - .NET 7 से NativeAOT compilation उपलब्ध है, जिससे VM के बिना भी managed code चलाया जा सकता है.
- C# भी Rust की तरह
-
NodeJS
- asynchronous tasks के लिए
Promise.allका उपयोग किया गया.
- asynchronous tasks के लिए
-
Python
- asynchronous tasks चलाने के लिए
asynciomodule का उपयोग किया गया.
- asynchronous tasks चलाने के लिए
-
Go
- concurrency को goroutine के जरिए लागू किया जाता है, और tasks के पूरा होने की प्रतीक्षा के लिए
WaitGroupका उपयोग किया जाता है.
- concurrency को goroutine के जरिए लागू किया जाता है, और tasks के पूरा होने की प्रतीक्षा के लिए
-
Java
- JDK 21 से virtual threads उपलब्ध हैं, जो goroutine जैसी अवधारणा है.
- GraalVM का उपयोग करके native image बनाई जा सकती है.
टेस्ट वातावरण
- हार्डवेयर: 13वीं पीढ़ी Intel(R) Core(TM) i7-13700K
- ऑपरेटिंग सिस्टम: Debian GNU/Linux 12 (bookworm)
- Rust: 1.82.0
- .NET: 9.0.100
- Go: 1.23.3
- Java: openjdk 23.0.1
- Java (GraalVM): java 23.0.1
- NodeJS: v23.2.0
- Python: 3.13.0
परिणाम
-
न्यूनतम मेमोरी उपयोग
- Rust, C# (NativeAOT), और Go native binary में compile होते हैं, इसलिए कम मेमोरी का उपयोग करते हैं.
- Java (GraalVM native image) ने भी अच्छा प्रदर्शन दिखाया, लेकिन अन्य statically compiled भाषाओं की तुलना में अधिक मेमोरी का उपयोग किया.
-
10K tasks
- Rust में मेमोरी उपयोग लगभग नहीं बढ़ा.
- C# (NativeAOT) ने भी कम मेमोरी का उपयोग किया.
- Go ने अपेक्षा से अधिक मेमोरी का उपयोग किया.
-
100K tasks
- Rust और C# ने अच्छा प्रदर्शन दिखाया.
- C# (NativeAOT) ने Rust से कम मेमोरी का उपयोग किया.
-
10 लाख tasks
- C# ने सभी भाषाओं को पीछे छोड़ते हुए सबसे कम मेमोरी का उपयोग किया.
- Rust भी मेमोरी दक्षता में बहुत अच्छा रहा.
- Go ने अन्य भाषाओं की तुलना में अधिक मेमोरी का उपयोग किया.
निष्कर्ष
- बड़ी संख्या में समवर्ती tasks, भले ही वे जटिल काम न कर रहे हों, फिर भी काफी मेमोरी खर्च कर सकते हैं.
- .NET और NativeAOT में सुधार स्पष्ट रूप से दिखाई देते हैं, और GraalVM से बनी Java native image भी मेमोरी दक्षता में उत्कृष्ट है.
- goroutine अभी भी resource consumption के मामले में अप्रभावी हैं.
परिशिष्ट
- Rust (
tokio) मेंjoin_allकी जगहforloop का उपयोग करके मेमोरी उपयोग आधा कर दिया गया. इस बेंचमार्क में Rust ने स्पष्ट बढ़त हासिल की.
1 टिप्पणियां
Hacker News राय
बेंचमार्क Node और Go के asynchronous processing तरीकों के अंतर को ठीक से नहीं दिखाता। Node
Promise.allका उपयोग करता है और Go goroutine का, इसलिए अंतर है। asynchronous I/O और CPU-bound कामों के memory usage के अंतर की तुलना करना दिलचस्प होगायह "10 सेकंड तक इंतज़ार करने वाला काम" और "10 सेकंड बाद जगाया जाने वाला काम" के बीच का अंतर समझाता है। Go कोड का memory usage दूसरे कोड की तुलना में काफ़ी अलग है
Go और Node की निष्पक्ष तुलना के लिए timer schedule करने वाले goroutine और timer signal संभालने वाले goroutine का उपयोग करने का सुझाव दिया गया है। यह भी कहा गया है कि Node तुलना में Bun और Deno का शामिल न होना अजीब है
बहुत सारे concurrent tasks काफ़ी memory consume कर सकते हैं, लेकिन अगर प्रति task data कुछ KB से ज़्यादा है तो scheduler का memory overhead लगभग नज़रअंदाज़ करने लायक होता है
"concurrent tasks" की परिभाषा के अनुसार memory usage बदल सकता है। एक efficient implementation में 1M concurrent tasks के लिए लगभग 200MB चाहिए
यह इंगित किया गया है कि memory usage में Go, Java की तुलना में 2 गुना से भी ज़्यादा पीछे है, और कहा गया है कि यह benchmark वास्तविक प्रोग्रामों का प्रतिनिधित्व नहीं करता
सरल code से भाषाओं की तुलना करना developers के लिए अनुचित हो सकता है, और सलाह दी गई है कि वास्तविक काम जोड़कर memory usage और scheduling के अंतर को मापा जाए
कहा गया है कि benchmarks अक्सर गलतियों से भरे होते हैं, और ऐसे benchmarks प्रकाशित करने वालों की मंशा समझ में नहीं आती
Java benchmark के गलत होने की संभावना बताई गई है, क्योंकि
ArrayListका initial size तय नहीं किया गया, जिससे बहुत से अनावश्यक objects बनते हैंयह समझाया गया है कि Rust का asynchronous code उम्मीद से पहले क्यों पूरा हो जाता है। क्योंकि
tokio::time::sleep()future के बनाए जाने के समय को track करता है