avatar
·4 dk okuma
Node.js Thread Pool Boyutu ve Performans Üzerindeki Etkileri

Node.js Thread Pool Boyutu ve Performans Üzerindeki Etkileri

Node.js, tek iş parçacıklı bir çalışma zamanı ortamı olarak bilinir, ancak bazı ağır hesaplamalar ve I/O operasyonları için arka planda bir thread pool kullanır. Bu makalede, Node.js'te thread pool boyutunun nasıl ayarlandığını, performansı nasıl etkilediğini ve bu süreçte karşılaşılabilecek bazı sorunları inceleyeceğiz. Ayrıca, örnek kodlar ve deneyler üzerinden bu kavramları detaylı olarak açıklayacağız.

Libuv Thread Pool Nedir?

Libuv, Node.js'in altında yatan I/O ve asenkron işlemleri yöneten bir kütüphanedir. Libuv, özellikle ağ işlemleri, dosya sistemi erişimleri ve bazı kriptografik işlemler gibi CPU yoğun işlemleri ana iş parçacığından alıp bir thread pool'a atayarak yönetir. Bu sayede, ana iş parçacığı diğer işlerle meşgulken ağır işlemler arka planda çalışabilir.

libuv thread pool

Thread Pool Varsayılan Boyutu

Varsayılan olarak, Node.js'te Libuv thread pool'unun boyutu dört thread'dir. Bu, dört asenkron işlemin paralel olarak çalışabileceği anlamına gelir. Ancak, bu boyut bazen yetersiz kalabilir ve performans sorunlarına yol açabilir.

libuv thread pool

Örnek Kod

const fs = require("node:fs");
const crypto = require("node:crypto");

console.log("Başlangıç");

fs.readFile("file.txt", "utf-8", (err, data) => {
  if (err) throw err;
  console.log(data);
});

console.log("Bitiş");

Yukarıdaki kodda, fs.readFile asenkron bir I/O işlemi olarak thread pool kullanırken, crypto.pbkdf2Sync senkron bir CPU yoğun işlemdir ve thread pool kullanmaz.

Thread Pool Boyutunu Artırma

Node.js'te thread pool boyutunu artırarak performansı iyileştirebiliriz. Bunu yapmak için process.env.UV_THREADPOOL_SIZE ortam değişkenini kullanabiliriz.

Örnek Kod

Aşağıdaki örnekte, thread pool boyutunu beş olarak ayarlayıp crypto.pbkdf2 fonksiyonunu beş kez çağırıyoruz:

const crypto = require("node:crypto");

process.env.UV_THREADPOOL_SIZE = 5; // Bu satırın üstte olduğundan emin olun

const start = Date.now();
const CALL_LIMIT = 5;

for (let i = 0; i < CALL_LIMIT; i++) {
  crypto.pbkdf2("password", "salt", 100000, 512, "sha512", () => {
    console.log(`Süre: ${Date.now() - start} ms`);
  });
}

console.log("async");

Bu kodda, beş asenkron pbkdf2 işlemi paralel olarak çalışır ve her biri thread pool'daki bir thread'i kullanır. Bu sayede işlemler daha hızlı tamamlanır.

Deneyler ve Gözlemler

  1. Varsayılan Boyut: 4 Thread

const crypto = require("node:crypto");

const start = Date.now();
const CALL_LIMIT = 5;

for (let i = 0; i < CALL_LIMIT; i++) {
  crypto.pbkdf2("password", "salt", 100000, 512, "sha512", () => {
    console.log(`Süre: ${Date.now() - start} ms`);
  });
}

console.log("async");

Bu durumda, beşinci işlem diğerlerinden daha uzun sürer çünkü ilk dört işlem kendi thread'lerinde çalışırken beşinci işlem, bir thread'in boşalmasını bekler.

  1. Thread Pool Boyutunu Artırma: 5 Thread

    const crypto = require("node:crypto");
    
    process.env.UV_THREADPOOL_SIZE = 5;
    
    const start = Date.now();
    const CALL_LIMIT = 5;
    
    for (let i = 0; i < CALL_LIMIT; i++) {
      crypto.pbkdf2("password", "salt", 100000, 512, "sha512", () => {
        console.log(`Süre: ${Date.now() - start} ms`);
      });
    }
    
    console.log("async");

    Bu durumda, tüm işlemler neredeyse aynı sürede tamamlanır, çünkü her işlem kendi thread'inde çalışır.

  2. CPU Çekirdeklerini Aşma: 16 Thread

    const crypto = require("node:crypto");
    
    process.env.UV_THREADPOOL_SIZE = 16;
    
    const start = Date.now();
    const CALL_LIMIT = 16;
    
    for (let i = 0; i < CALL_LIMIT; i++) {
      crypto.pbkdf2("password", "salt", 100000, 512, "sha512", () => {
        console.log(`Süre: ${Date.now() - start} ms`);
      });
    }
    
    console.log("async");

    Bu durumda, işlemler daha uzun sürer çünkü işletim sistemi, 16 thread'i 8 çekirdek üzerinde dağıtmak zorunda kalır. Bu, her işlemin tamamlanma süresini artırır.

Gerçek Dünya Senaryosu: Yüksek Trafikli Web Sunucusu

Bir web sunucusu örneğini ele alalım. Sunucu, yoğun olarak CPU'yu kullanan işlemler gerçekleştiriyor ve bu işlemler Node.js'in thread pool'u tarafından yönetiliyor. Eğer sunucuda aynı anda çok sayıda CPU yoğun işlem gerçekleşiyorsa ve thread pool yetersizse, işlemler sırayla işlenir ve yanıt süreleri uzar. Bu durumda, thread pool boyutunu, sunucunuzun CPU çekirdek sayısına uygun bir şekilde artırmak, paralel işlem kapasitesini artırır ve işlem sürelerini azaltır. Ancak, CPU çekirdek sayısından fazla bir thread pool boyutu ayarlamak, işlemciyi gereksiz yere yorar ve performansı düşürebilir.

libuv thread pool

CPU Çekirdek Sayısı ve Thread Pool Boyutunun Dengesi

CPU çekirdek sayısından daha fazla bir thread pool boyutu ayarlamak, performans üzerinde olumsuz etkilere yol açabilir. Örneğin, 8 çekirdekli bir bilgisayarda thread pool boyutunu 16'ya çıkarmak, işletim sisteminin daha fazla thread'i yönetmek için daha fazla kaynak harcaması gerektirdiği anlamına gelir. Bu durum, işlem süresinin artmasına neden olur.

libuv thread pool

Sonuç

Thread pool boyutunu artırmak, doğru şekilde yapıldığında performansı iyileştirebilir. Ancak, bu ayarın CPU çekirdek sayısına göre yapılması önemlidir. Aksi takdirde, beklenmedik performans düşüşleri yaşanabilir.

Özet

  • Node.js, bazı ağır işlemleri Libuv thread pool kullanarak yönetir.

  • Varsayılan olarak dört thread ile çalışır, ancak process.env.UV_THREADPOOL_SIZE kullanarak bu boyutu artırabiliriz.

  • Thread pool boyutunu artırmak performansı iyileştirebilir, ancak thread sayısı CPU çekirdek sayısını aşarsa performans düşüşü yaşanabilir.

Bu makalede, Node.js'te thread pool boyutunu nasıl ayarlayacağınızı ve performansı nasıl optimize edeceğinizi öğrendik. Bu bilgiler, uygulamalarınızın daha verimli çalışmasına yardımcı olabilir.