Tek thread ile çalışmanın avantaj ve dezavantajları

Önceki bir flood’da JavaScript’in tek thread (single thread) ile işleri nasıl eş zamanlı yürüttüğüne değinmiştik. Bu flood'da ise tek thread ile çalışmanın bazı avantaj ve dezavantajlarını ele alacağız.

İşletim sistemleri, işlemciyi çalışan uygulamalar arasında belirlenen bir planlama algoritması (Round Robin Scheduling, Priority Based Scheduling, vb) ile paylaştırarak bütün proseslerin işlemciyi kullanmasını sağlarlar. Seçilecek planlama algoritması (Scheduling Algorithm) sistemin tipine göre belirlenir. Round Robin ile uygulamalara eşit olarak dağıtılan işlemci zamanı Priority Based ile uygulamaların önceliklerine göre kullandırılır. Gerçek Zamanlı sistemlerde Priority Based Scheduling kullanılır çünkü sistemin asıl işi önceliklendirilir.

İşletim sistemleri, Scheduler adı verilen bileşenler ile Scheduling işlevini yürütürler. Scheduling işlemi temel olarak, Scheduler'ın işlemci üzerinde koşmakta olan ve kendisine ayrılan süre dolan thread'i uyutarak, koşmaya hazır başka bir thread'e işlemciyi vermesidir. Scheduler zamanı dolan thread'i uyuturken onun state'ini (register, SP - stack pointer, PC - program counter, TLB - memory map, vb) kaydedip, koşmaya hazırlanan thread'in state'ini yükler, bu işlem Context Switch olarak adlandırılır ve maliyetli bir işlemdir.

Linux’ta, thread’ler de proses’lere benzer şekilde koşturulur. Aynı proses içindeki iki thread bazı memory alanlarını ortak kullandığından dolayı işlemci aynı proses içindeki iki thread arasında el değiştirirken oluşan Context Switch maliyeti işlemcinin iki farklı proses arasında el değiştirme maliyetinden küçüktür. Bundan dolayı, eğer proseslerin sağladığı izolasyon gerekli değilse kaynakların etkin kullanımı açısından uygulama tasarımında multi-prosess yerine multithreaded yaklaşımın benimsenmesi anlamlı olacaktır.

Yukarıdaki uzun girizgahtan da anlaşılacağı üzere single-threaded çalışmanın en önemli avantajlarından biri işlerin tek thread ile yürümesi dolayısıyla Context Switch yükünün (overhead) ortadan kalkmasıdır. Özellikle eş zamanlı çalışan thread sayısının oldukça arttığı durumlarda (> 100) Context Switch'e harcanan zaman işlemci zamanının önemli bir kısmını almaya başlayacak ve verimli olarak kullanılabilecek bu işlemci zamanı thread'ler arasında geçiş yapmak üzere harcanacaktır.

Single-threaded uygulama tasarımında önceki flood'da bahsedilen Run-to-Completion özelliği ile birlikte, aynı anda sadece bir thread işini bitirene kadar koştuğu için semaphore, lock benzeri race condition önleyici senkronizasyon yapıları kullanmak gerekmez. Bu sayede uygulamada daha az senkronizasyon kaynaklı programlama hatası ortaya çıkar. Örnek olarak Mars keşif aracı PathFinder tek thread ile çalışsaydı önceki bir flood'da anlatıldığı gibi Priority Inversion problemi ile karşılaşmayacaktı. https://medium.com/@gokhansengun/5b6ebe771d55

Single-threaded sistemlerin en önemli dezavantajlarından biri uygulamanın kooperatif biçimde çalışmasına bel bağlamasıdır. İşlemcinin koşturduğu kod bloğunun çalışması bitmeden başka bir kod koşturulamayacağından uygulamadaki bir sonsuz döngü bütün uygulamayı kilitleyebilir. Benzer durum multithreaded uygulamada sadece ilgili thread'in işlev gösterememesi ve işlemci kaynaklarının gereksiz kullanımı ile genel sistem yavaşlaması ile atlatılabilecekken single-threaded bir uygulamada uygulamanın bir bütün olarak işlevsiz kalmasına neden olur.

Anlaşılacağı üzere Real Time uygulamalarda single-threading kullanılamaz çünkü bu tip uygulamalarda sistemin gerçek zamanlı olarak tepki vermesi gereken işlemler vardır. Örneğin bir arabanın fren sistemini kontrol eden elektronik devrenin birkaç milisaniye içinde aktive edilmesi gereken bir durumda single-thread'in o anda koşturmakta olduğu ve 20 milisaniye sürecek CD çalardan sonraki şarkıyı yükleme işlemi beklenemez. Dolayısıyla aslında Mars keşif aracı PathFinder'da tek thread'li bir mimari kullanmak mümkün değildir :-)

Tek thread olarak çalışan uygulamaları popüler hale getiren ve muhtemelen bu flood’u yazdıran özellikleri ise I/O ağırlıklı, yani veri alış verişinin yoğun olduğu uygulamalarda oldukça iyi ölçeklenebilmeleridir. Web sunucular, proxy sunucular ve uygulama sunucular genellikle I/O ağırlıklı uygulamalardır. I/O ağırlıklı uygulamalar hızlı bir veri işlemeden sonra girdi/çıktı bekledikleri için ve bu kısa işlemler arasında işlemci üzerinde Context Switch oluşturmadıkları için yüksek yüklerde çok iyi biçimde performans göstermektedirler.

CPU ağırlıklı işlemlerde (video işleme, matematiksel işlemler, vb) ise tek thread olarak çalışan uygulamaların ölçekleme özellikleri anlamını kaybeder, aksine tek thread uzun süreler boyunca kullanıldığı için kullanıcı deneyimi olumsuz anlamda etkilenir. Tek thread ile çalışan birçok sistem CPU ağırlıklı işlemlerin yapılabilmesi için farklı mekanizmalar sunmaktadır. Tarayıcılarda, JavaScript’in CPU ağırlıklı işlemlerin yapılabilmesi için Web Worker'lar kullanıma sunulmuştur.

Modern bilgisayarlarda birden fazla işlemci ve her işlemcide bir veya birden fazla çekirdek (core) bulunur. Tek thread'li uygulamalar ek bir efor olmadan çok çekirdekli mimariden istifade edemezler. Tek bir thread çalıştığı için aynı anda sadece bir çekirdek kullanabilirler. Özellikle sunucu tarafındaki uygulamalarda (JavaScript için Node.js) bütün çekirdekleri kullanmak performans açısından elzem olduğu için sunucuda çekirdek sayısı kadar Node.js uygulaması çalıştırılır ya da uygulama içerisinde core sayısı kadar worker yaratılır.

Web sunucu ve proxy sunucu olarak popüler olarak kullanılan Nginx de single-threaded olarak çalışmaktadır. Nginx, default konfigürasyonunda worker_processes auto; ile koşturulduğu sunucudaki çekirdek sayısı kadar worker proses açarak istekleri bu proseslerle karşılar.

single-threaded olarak çalışan Nginx'in, multithreaded olarak çalışan Apache web sunucu ile eş zamanlı 50 kullanıcıyla 5k'lık bir dosya istenerek yapılan karşılaştırması aşağıda bulunabilir. https://bit.ly/2eERS8V

Son olarak, JavaScript’in işleri tek thread ile nasıl paralel yürüttüğünü anımsamak için önceki bu flood’a https://medium.com/@gokhansengun/24f92f71f9bd göz atabilirsiniz.