हाँ, मैंने यह कहा!
कॉलबैक को एसिंक पर समानांतर चलाने/समानांतर में प्रतीक्षा करने पर 4.8 गुना तेज होता है। और जब हम अनुक्रमिक कॉलबैक चलाते हैं तो यह केवल 1.9 गुना तेज होता है।
मेरे संदिग्ध परीक्षण के बारे में कुछ उपयोगी और दयालु टिप्पणियाँ मिलने के बाद मैंने इस लेख को कुछ हद तक संशोधित किया है। 😂🙏
इसके लिए आपको धन्यवाद
रिकार्डो लोपेज औररयान पो बेंचमार्क को सही दिशा में ले जाने के लिए समय निकालने के लिए। मेरी पहली ग़लती यह थी कि मैं वास्तव में कोड के निष्पादन के ख़त्म होने का इंतज़ार नहीं कर रहा था, जिससे परिणाम बेहद ख़राब हो गए। दूसरा यह कि मैं अनुक्रमिक रनटाइम के समानांतर तुलना कर रहा था, जो बेंचमार्क को बेकार बना देता है।
तो यह राउंड 2 है जो मेरी प्रारंभिक त्रुटियों को संबोधित करता है। पहले, मैंने कहा था:
मूल रूप से, मैंने लिखा था कि यदि हम कॉलबैक पर वापस जाएँ तो NodeJS 34.7 गुना तेज़ है! 🤣गलत.
मेरे पहले के ख़राब बेंचमार्क जितना प्रभावशाली नहीं है (और संदर्भ के लिए टिप्पणियाँ देखें), लेकिन फिर भी एक बड़ा अंतर है।
किसी फ़ाइल को पढ़ते समय मैंने 10,000 बार कॉलबैक की तुलना वादों और एसिंक/प्रतीक्षा से की। और शायद यह एक मूर्खतापूर्ण परीक्षण है लेकिन मैं जानना चाहता था कि I/O पर कौन तेज़ है।
फिर मैंने अंततः Node.js में कॉलबैक की तुलना गो से की!
अब सोचो कौन जीता?
मैं मतलबी नहीं बनूँगा. टीएलडीआर । गोलांग!
कम बेहतर है। परिणाम
ms
में हैं.
अब, सच्चे बेंचमार्कर? इस पर मेरे साथ सहजता से पेश आओ। लेकिन कृपया मुझे एक बेहतर इंसान बनाने के लिए अपनी टिप्पणियाँ अवश्य छोड़ें।
और यह मुझे परेशान करता है।
क्योंकि धीमे का मतलब क्या है? सभी बेंचमार्क की तरह, मेरा संदर्भ प्रासंगिक है।
मैंने इसके बारे में पढ़ना शुरू किया
लेकिन मुख्य बात जो मैंने समझी है वह यह है कि Node.js I/O कार्यों को एक कतार में भेजता है जो मुख्य Node.js निष्पादन योग्य थ्रेड के बाहर बैठता है। यह कतार चलती रहती है
हालाँकि, वादे मुख्य, एकल निष्पादन योग्य थ्रेड में संभाले जाते हैं। और async/प्रतीक्षा, ठीक है, वादा करता है लेकिन अब अवरोधन जोड़ दिया गया है।
आइए इसका परीक्षण करें।
सबसे पहले। मेरी मशीन ! के साथ काम करने के पूरक
MacBook Pro (14-inch, 2021) Chip Apple M1 Pro Memory 32 GB Cores 10 NodeJS v20.8.1
तो हमारे पास मूल संदेश, Hello, world
के साथ एक text.txt
फ़ाइल है।
echo "Hello, world" > text.txt
और हम इस टेक्स्ट फ़ाइल को मूल Node.js का उपयोग करके पढ़ेंगे, जिसका अर्थ है, शून्य नोड मॉड्यूल निर्भरताएँ क्योंकि हम ब्रह्मांड में सबसे भारी वस्तुओं के साथ गति को कम नहीं करना चाहते हैं।
सबसे पहले, आइए समानांतर कॉलबैक से शुरुआत करें। मुझे इस बात में दिलचस्पी है कि एक ही फ़ाइल को एक ही बार में जितनी जल्दी संभव हो सके कितनी जल्दी पढ़ा जा सकता है। और समानांतर से तेज़ क्या है?
// > file-callback-parallel.test.mjs import test from 'node:test'; import assert from 'node:assert'; import fs from "node:fs"; test('reading file 10,000 times with callback parallel', (t, done) => { let count = 0; for (let i = 0; i < 10000; i++) { fs.readFile("./text.txt", { encoding: 'utf-8'}, (err, data) => { assert.strictEqual(data, "Hello, world"); count++ if (count === 10000) { done() } }) } });
दूसरा, हमारे पास फिर से कॉलबैक हैं, लेकिन अनुक्रमिक (या बल्कि अवरुद्ध)। मुझे इस बात में दिलचस्पी है कि एक ही फ़ाइल को क्रमिक रूप से कितनी जल्दी पढ़ा जा सकता है। काफी समय से कॉलबैक कॉल नहीं करने के बाद, दोबारा प्रयास करना मजेदार था। हालाँकि, यह सुंदर नहीं दिखता।
// > file-callback-blocking.test.mjs import test from 'node:test'; import assert from 'node:assert'; import fs from "node:fs"; let read = (i, callback) => { fs.readFile("./text.txt", { encoding: 'utf-8'}, (err, data) => { assert.strictEqual(data, "Hello, world"); i += 1 if (i === 10000) { return callback() } read(i, callback) }) } test('reading file 10,000 times with callback blocking', (t, done) => { read(0, done) });
फिर हमारे पास async/प्रतीक्षा है। Nodejs के साथ काम करने का मेरा पसंदीदा तरीका।
यह उतना ही समानांतर है जितना मैं async/प्रतीक्षा के साथ प्राप्त कर सकता हूं। मैं सभी readFile
ऑपरेशंस को एक सरणी में लोड करता हूं और Promise.all
उपयोग करके उन सभी का इंतजार करता हूं।
// > file-async-parallel.test.mjs import test from 'node:test'; import assert from 'node:assert'; import fs from "node:fs/promises"; test('reading file 10,000 times with async parallel', async (t) => { let allFiles = [] for (let i = 0; i < 10000; i++) { allFiles.push(fs.readFile("./text.txt", { encoding: 'utf-8'})) } return await Promise.all(allFiles) .then(allFiles => { return allFiles.forEach((data) => { assert.strictEqual(data, "Hello, world"); }) }) });
यह लिखने में सबसे आसान और सबसे संक्षिप्त था।
// > file-async-blocking.test.mjs import test from 'node:test'; import assert from 'node:assert'; import fs from "node:fs/promises"; test('reading file 10,000 times with async blocking', async (t) => { for (let i = 0; i < 10000; i++) { let data = await fs.readFile("./text.txt", { encoding: 'utf-8'}) assert.strictEqual(data, "Hello, world"); } });
अंततः, हमारे पास एसिंक/प्रतीक्षा के बिना वादे हैं। मैंने लंबे समय से उन्हें async/await
के पक्ष में उपयोग करना बंद कर दिया है, लेकिन मुझे इसमें दिलचस्पी थी कि वे प्रदर्शन कर रहे थे या नहीं।
// > file-promise-parallel.test.mjs import test from 'node:test'; import assert from 'node:assert'; import fs from "node:fs/promises"; test('reading file 10,000 times with promise parallel', (t, done) => { let allFiles = [] for (let i = 0; i < 10000; i++) { allFiles.push(fs.readFile("./text.txt", { encoding: 'utf-8'})) } Promise.all(allFiles) .then(allFiles => { for (let i = 0; i < 10000; i++) { assert.strictEqual(allFiles[i], "Hello, world"); } done() }) });
फिर, हम सभी readFile
ऑपरेशंस के निष्पादन की प्रतीक्षा करना चाहते हैं।
// > file-promise-blocking.test.mjs import test from 'node:test'; import assert from 'node:assert'; import fs from "node:fs/promises"; test('reading file 10,000 times with promises blocking', (t, done) => { let count = 0; for (let i = 0; i < 10000; i++) { let data = fs.readFile("./text.txt", { encoding: 'utf-8'}) .then(data => { assert.strictEqual(data, "Hello, world") count++ if (count === 10000) { done() } }) } });
और वोइला! परिणाम 🎉! बेहतर पढ़ने के लिए मैंने इसे कुछ बार चलाया भी।
मैंने प्रत्येक परीक्षण यह करके चलाया:
node --test <file>.mjs
किसी फ़ाइल को कॉलबैक के साथ 10,000 बार पढ़ना समानांतर में एसिंक/प्रतीक्षा की तुलना में 5.8 गुना अधिक तेज़ है! यह समानांतर वादों की तुलना में 4.7 गुना तेज़ है!
तो, Node.js भूमि में, कॉलबैक अधिक प्रदर्शनशील हैं !
खैर, मैं गो में नहीं लिखता, इसलिए यह वास्तव में भयानक कोड हो सकता है क्योंकि मैंने चैटजीपीटी से मेरी मदद करने के लिए कहा और फिर भी, यह काफी अच्छा लगता है ।
हे हो। चल दर। हमारा गोलांग कोड।
package main import ( "fmt" "io/ioutil" "time" ) func main() { startTime := time.Now() for i := 0; i < 10000; i++ { data, err := ioutil.ReadFile("./text.txt") if err != nil { fmt.Printf("Error reading file: %v\n", err) return } if string(data) != "Hello, world" { fmt.Println("File content mismatch: got", string(data), ", want Hello, world") return } } duration := time.Since(startTime) fmt.Printf("Test execution time: %v\n", duration) }
और हम इसे इस प्रकार चलाते हैं:
go run main.go
और परिणाम?
Test execution time: 58.877125ms
🤯 क्रमिक कॉलबैक का उपयोग करके Go, Node.js से 4.9 गुना तेज़ है। Node.js केवल समानांतर निष्पादन के करीब आता है।
Node.js Async/await Go की तुलना में 9.2x धीमा है।
इसलिए हां। Node.js धीमा है. फिर भी, 300 एमएस से कम की 10,000 फाइलों का उपहास नहीं किया जाना चाहिए। लेकिन मैं गो की तेजी से दंग रह गया हूँ!
मेरे पास वास्तव में भयानक बेंचमार्क थे। रिकार्डो और रयान को फिर से धन्यवाद।
हाँ मैंने किया। उम्मीद है कि अब वे बेहतर हैं.
लेकिन आप पूछ सकते हैं कि वास्तव में एक ही फ़ाइल को बार-बार कौन पढ़ेगा? लेकिन चीज़ों के बीच सापेक्ष परीक्षण के लिए, मुझे आशा है कि यह एक उपयोगी तुलना होगी।
मैं यह भी नहीं जानता कि Node.js कितने थ्रेड्स का उपयोग कर रहा है।
मुझे नहीं पता कि मेरे सीपीयू कोर गो बनाम नोड.जेएस प्रदर्शन को कैसे प्रभावित करते हैं।
मैं बस एक कोर वाली AWS मशीन किराए पर ले सकता हूं और तुलना कर सकता हूं।
क्या ऐसा इसलिए है क्योंकि मैं Mac M1 पर हूँ?
Node.js Linux या...Windows पर कैसा प्रदर्शन करेगा? 😱
और व्यावहारिकता है, हाँ, किसी फ़ाइल को पढ़ना एक बात है, लेकिन कुछ बिंदु पर, आपको फ़ाइल में डेटा के साथ कुछ करने के लिए फ़ाइल को पढ़ने के लिए वैसे भी इंतजार करना होगा। इसलिए, मुख्य थ्रेड पर गति अभी भी बहुत महत्वपूर्ण है।
मेरा मतलब है, क्या आप सचमुच चाहते हैं?
मुझें नहीं पता। मैं निश्चित रूप से किसी को यह नहीं बताना चाहता कि क्या करना है।
लेकिन मुझे एसिंक/वेट्स का साफ़ सिंटैक्स पसंद है।
वे बेहतर दिखते हैं.
वे बेहतर पढ़ते हैं.
मैं जानता हूं कि यहां बेहतर व्यक्तिपरक है, लेकिन मुझे कॉलबैक-हेल याद है, और जब वादे अस्तित्व में आए तो मैं आभारी था। इसने जावास्क्रिप्ट को सहने योग्य बना दिया।
अब, कॉलबैक के साथ, और एसिंक/प्रतीक्षा के साथ, गोलांग स्पष्ट रूप से Node.js से 9.2x अधिक तेज़ है! इसलिए यदि हम अच्छी पठनीयता और प्रदर्शन चाहते हैं, तो गोलांग विजेता है। हालाँकि, मुझे यह जानना अच्छा लगेगा कि गोलांग हुड के नीचे कैसा दिखता है।
कोई भी. यह मजेदार था। यह मुझे यह समझने में मदद करने के लिए एक अभ्यास था कि इवेंट लूप में कॉलबैक और I/O कैसे काम करते हैं।
क्या Node.js धीमा है? या क्या हम केवल धीमे मोड पर Node.js का उपयोग कर रहे हैं?
संभवतः जहां प्रदर्शन मायने रखता है, गोलांग छलांग लगाने लायक है। मैं निश्चित रूप से भविष्य में गोलांग का और अधिक उपयोग करने पर विचार करूँगा।
यहाँ भी दिखाई देता है.