paint-brush
NodeJS: Geri Aramalara Geri Dönerseniz 4,8 Kat Daha Hızlı!ile@gemmablack
20,095 okumalar
20,095 okumalar

NodeJS: Geri Aramalara Geri Dönerseniz 4,8 Kat Daha Hızlı!

ile Gemma Black9m2024/02/12
Read on Terminal Reader

Çok uzun; Okumak

Geri aramalar, eşzamansız/paralel olarak beklemede paralel olarak çalıştırıldığında 4,8 kat daha hızlıdır. Sıralı geri aramalar yaptığımızda yalnızca 1,9 kat daha hızlıyız.
featured image - NodeJS: Geri Aramalara Geri Dönerseniz 4,8 Kat Daha Hızlı!
Gemma Black HackerNoon profile picture

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 Ve Ryan 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.

Peki tam olarak neyi test ettim?

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.

Herkes Node.js'nin yavaş olduğunu söylüyor!

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 Olay Döngüsü , sadece anlamaya başlamak için bile nasıl çalışır .


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 saf C . Bir dizi iş parçacığı potansiyel olarak bu G/Ç işlemlerini gerçekleştirebilir. İşte Node.js'nin G/Ç'yi yöneterek parlayabileceği yer burasıdır.


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.

6 farklı kuyruktan oluşan olay döngüsü. Kaynak: https://www.builder.io/blog/visual-guide-to-nodejs-event-loop

Peki geri aramalar sözlerden daha mı hızlı?

Hadi test edelim.


İlki. Benim makinem ! Birlikte çalışmanın tamamlayıcıları Kamma . Hangi kaynaklarla çalıştığımıza dikkat etmek önemlidir. Bol miktarda bellek ve CPU.

 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.

Geri aramalar

Paralel geri aramalar

Ö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() } }) } });

Sıralı geri aramalar

İ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) });

Eşzamansız/Beklemede

Sonra zaman uyumsuz/bekliyoruz. Nodejs ile çalışmanın en sevdiğim yolu.

Paralel eşzamansız/beklemede

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"); }) }) });

Sıralı Eşzamansız/Bekleme

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"); } });

Vaatler

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.

Paralel vaatler

 // > 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() }) });

Sıralı vaatler.

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 !

Artık Go, Node.js'den daha mı hızlı?

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ı!

Şimdi sadece bir yan not. Kötü kriterlerim var mı?

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.

Şimdi, gerçekten geri aramaları kullanmak istiyor musunuz?

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ı.

Oturumu kapatmak için

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.