Evet dedim!
Geri aramalar, eşzamansız/paralel olarak beklemede paralel olarak çalıştırıldığında 4,8 kat daha hızlıdır. Ve sıralı geri aramaları çalıştırdığımızda yalnızca 1,9 kat daha hızlı.
Tehlikeli testim hakkında bazı yararlı ve nazik yorumlar aldıktan sonra bu makaleyi biraz değiştirdim. 😂🙏
Sanada teşekkürler
Ricardo Lopes VeRyan Poe Kriterleri doğru yöne yönlendirmek için zaman ayırdığınız için. İlk hatam aslında kodun yürütülmesinin bitmesini beklemememdi, bu da sonuçları çılgınca çarpıttı. İkincisi, paralel çalışma sürelerini sıralı çalışma zamanlarıyla karşılaştırıyor olmamdı, bu da kriterleri değersiz kılıyordu.
Bu, ilk hatalarımı ele alan 2. tur. Daha önce şunu söylemiştim:
Başlangıçta, geri aramalara geri dönersek NodeJS'nin 34,7 kat daha hızlı olduğunu yazmıştım! 🤣 Yanlış.
Daha önceki kötü kriterlerim kadar etkileyici değil (ve bağlam için yorumlara bakın), ancak yine de oldukça büyük bir fark var.
Bir dosyayı okurken 10.000 kez geri aramaları vaatlerle ve eşzamansız/beklemeyle karşılaştırdım. Belki bu aptalca bir test ama bilmek istedim, hangisi G/Ç'de daha hızlıdır.
Sonra nihayet Node.js'deki geri aramaları Go!
Şimdi tahmin edin kim kazandı?
Kötü niyetli olmayacağım. TLDR . Golang!
Alçak daha iyi. Sonuçlar
ms
cinsindendir.
Şimdi, gerçek kıyaslamacılar mı? Bu konuda bana nazik davran. Ama lütfen beni daha iyi bir insan yapmak için yorumlarınızı bırakın.
Ve bu beni rahatsız ediyor.
Çünkü yavaş ne anlama geliyor? Tüm kıyaslamalarda olduğu gibi benimki de bağlamsaldır.
hakkında okumaya başladım
Ancak anladığım asıl şey, Node.js'nin G/Ç görevlerini ana Node.js çalıştırılabilir iş parçacığının dışında bulunan bir kuyruğa aktardığıdır. Bu sıra devam ediyor
Ancak vaatler ana, tek yürütülebilir iş parçacığında ele alınır. Ve eşzamansız/beklemede, vaat ediyor ama şimdi engelleme eklendi.
Hadi test edelim.
İlki. Benim makinem ! Birlikte çalışmanın tamamlayıcıları
MacBook Pro (14-inch, 2021) Chip Apple M1 Pro Memory 32 GB Cores 10 NodeJS v20.8.1
Yani, orijinal bir mesaj içeren bir text.txt
dosyamız var: Hello, world
.
echo "Hello, world" > text.txt
Ve bu metin dosyasını yerel Node.js kullanarak okuyacağız, bu da sıfır düğüm modülü bağımlılığı anlamına gelir çünkü evrendeki en ağır nesnelerle hızı düşürmek istemiyoruz.
Öncelikle paralel geri aramalarla başlayalım. Aynı dosyanın aynı anda ne kadar hızlı okunabileceğiyle ilgileniyorum. Peki paralelden daha hızlı olan nedir?
// > 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() } }) } });
İkincisi, tekrar geri aramalarımız var, ancak sıralı (veya daha doğrusu engelleyici). Aynı dosyanın sırayla ne kadar hızlı okunabileceğiyle ilgileniyorum. Yıllardır geri aramaları çağıran geri aramalar yapmadığım için bunu tekrar denemek eğlenceliydi. Yine de pek hoş görünmüyor.
// > 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) });
Sonra zaman uyumsuz/bekliyoruz. Nodejs ile çalışmanın en sevdiğim yolu.
Async/await ile alabildiğim kadar paralel. Tüm readFile
işlemlerini bir diziye yüklüyorum ve hepsini Promise.all
kullanarak bekliyorum.
// > 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"); }) }) });
Bu yazılması en kolay ve en kısa olanıydı.
// > 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"); } });
Son olarak, async/await olmadan sözlerimiz var. Uzun zamandır bunları async/await
lehine kullanmayı bıraktım ancak performans gösterip göstermedikleriyle ilgileniyordum.
// > 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() }) });
Yine tüm readFile
işlemlerinin yürütülmesini beklemek istiyoruz.
// > 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() } }) } });
Ve işte! Sonuçlar 🎉! Hatta daha iyi okuyabilmek için birkaç kez çalıştırdım.
Her testi şunları yaparak yaptım:
node --test <file>.mjs
Bir dosyayı geri aramalarla 10.000 kez okumak, paralel olarak eşzamansız/beklemede olduğundan 5,8 kat daha hızlıdır! Aynı zamanda paralel vaatlerden 4,7 kat daha hızlıdır!
Yani Node.js topraklarında geri aramalar daha performanslıdır !
Go'da yazmıyorum, yani bu gerçekten berbat bir kod olabilir çünkü ChatGPT'den bana yardım etmesini istedim ama yine de oldukça iyi görünüyor .
Hey ho. Hadi gidelim. Golang kodumuz.
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) }
Ve bunu şu şekilde çalıştırıyoruz:
go run main.go
Peki sonuçlar?
Test execution time: 58.877125ms
🤯 Go, sıralı geri aramaları kullanan Node.js'den 4,9 kat daha hızlıdır. Node.js yalnızca paralel yürütmeyle yaklaşır.
Node.js Async/await, Go'dan 9,2 kat daha yavaştır.
Yani evet. Node.js daha yavaştır. Yine de 300 ms'nin altındaki 10.000 dosyayla alay edilecek bir şey değil. Ama Go'nun hızı beni utandırdı!
Gerçekten korkunç Kriterlerim vardı. Ricardo ve Ryan'a tekrar teşekkür ederim.
Evet yaptım. Umarım şimdi daha iyilerdir.
Ama aynı dosyayı tekrar tekrar kim okuyacak diye sorabilirsiniz. Ancak şeyler arasındaki göreceli bir test için bunun yararlı bir karşılaştırma olduğunu umuyorum.
Ayrıca Node.js'nin kaç iş parçacığı kullandığını da bilmiyorum.
CPU çekirdeklerimin Go vs Node.js performansını nasıl etkilediğini bilmiyorum.
Tek çekirdekli bir AWS makinesi kiralayıp karşılaştırabilirim.
Mac M1 kullandığım için mi?
Node.js Linux veya Windows'ta nasıl performans gösterir? 😱
Ve evet, bir dosyayı okumak bir şeydir, ancak bir noktada, dosyadaki verilerle bir şeyler yapmak için yine de dosyanın okunmasını beklemeniz gerekir. Bu nedenle, ana iş parçacığında hız hala oldukça önemlidir.
Demek istediğim, gerçekten istiyor musun?
Bilmiyorum. Kesinlikle kimseye ne yapacağımı söylemek istemiyorum.
Ancak async/awaits'in temiz sözdizimini seviyorum.
Daha iyi görünüyorlar.
Daha iyi okuyorlar.
Burada daha iyisinin öznel olduğunu biliyorum ama geri aramayı hatırlıyorum ve sözler ortaya çıktığında minnettardım. Javascript'i katlanılabilir hale getirdi.
Artık Golang, geri aramalar ve zaman uyumsuz/bekleme özellikleriyle Node.js'den en iyi durumda 9,2 kat daha hızlıdır! Yani iyi okunabilirlik ve performans istiyorsak kazanan Golang'dır. Yine de Golang'ın kaputun altında nasıl göründüğünü öğrenmeyi çok isterim.
Her kimse. Bu komikti. Olay Döngüsünde geri aramaların ve G/Ç'nin nasıl çalıştığını anlamama yardımcı olacak bir alıştırmaydı.
Node.js yavaş mı? Yoksa Node.js'yi sadece yavaş modda mı kullanıyoruz?
Muhtemelen performansın önemli olduğu yerde Golang atlamaya değer. Gelecekte kesinlikle Golang'ı kullanmaya daha fazla bakacağım.
Ayrıca burada görünür.