Özet:
- Python'un
subprocessmodülü vepsutilkütüphanesi, son 15 yıldır süreç sonlanmasını beklerken (wait())sleepilewaitpidçağrılarını tekrarlayan verimsiz bir 'busy-loop polling' yöntemi kullanıyordu. - Bu yöntem gereksiz CPU wake-up'larına, pil tüketimine, süreç sonlanmasını algılamada gecikmeye (latency) yol açıyor ve çok sayıda süreci izlerken ölçeklenebilirlik açısından zayıf kalıyordu.
- Son güncellemeyle birlikte Linux'ta
pidfd_open()vepoll(), BSD/macOS'ta isekqueue()kullanılarak gerçek bir 'olay tabanlı bekleme (event-driven waiting)' hayata geçirildi. - Windows zaten
WaitForSingleObjectkullandığı için burada bir değişiklik yok; ancak POSIX sistemlerde gereksiz context switching ortadan kalkıyor ve CPU kullanımı '0'a yakınsıyor.
Ayrıntılı özet:
1. 15 yıldır süren sorun: Busy-loop polling
Python 3.3'te subprocess.Popen.wait() için timeout parametresi eklendiğinden beri, Python standart kütüphanesi ve yaygın kullanılan psutil kütüphanesi süreç sonlanmasını beklemek için verimsiz bir yöntem kullanıyordu.
Önceki mantık basitti ama verimsizdi:
waitpid(WNOHANG)ile süreç durumunu kontrol et (non-blocking)- Süreç bitmediyse kısa süre
sleep()yap (exponential backoff uygulanır) -
- adıma dön ve tekrarla
# Önceki yöntem (kavramsal kod)
import time, os
def wait_busy(pid, timeout):
delay = 0.0001
while True:
# Sürecin bitip bitmediğini kontrol et (polling)
if os.waitpid(pid, os.WNOHANG) == (pid, status):
return status
time.sleep(delay)
delay = min(delay * 2, 0.040) # bekleme süresini en fazla 40ms'e kadar artır
Bu yöntemin 3 kritik dezavantajı var.
- CPU wake-up'ları: Bekleme süresi artırılsa bile sistemin durumu kontrol etmek için periyodik olarak uyanması gerekir; bu da CPU çevrimlerini boşa harcar ve enerji tüketir.
- Latency (gecikme): Sürecin gerçekten sonlandığı an ile
sleep'ten uyanılıp bunun algılandığı an arasında kaçınılmaz bir zaman farkı oluşur. - Scalability (ölçeklenebilirlik): Aynı anda yüzlerce ya da binlerce sürecin izlenmesi gereken sunucu ortamlarında bu ek yük hızla büyür.
2. Çözüm: POSIX sistemler için olay tabanlı bekleme (Event-driven Waiting)
Tüm POSIX sistemleri, dosya tanımlayıcılarının (file descriptor) durum değişimlerini algılamak için mekanizmalar (select, poll, epoll, kqueue) sunar. Python ve psutil, yakın zamanda bunu süreç PID algılamasında kullanacak şekilde geliştirildi.
- Linux: 2019'da Linux 5.3 çekirdeğine eklenen
pidfd_open()sistem çağrısı kullanılıyor. Bu çağrı, süreç PID'sini işaret eden bir dosya tanımlayıcısı döndürüyor; bu tanımlayıcıpoll()ya daepoll()içine kaydedilerek süreç sonlanma olayı izlenebiliyor. (osmodülüne Python 3.9'dan itibaren eklendi) - BSD / macOS: Süreç olaylarını verimli biçimde izlemek için
kqueue()sistem çağrısınınEVFILT_PROCfiltresi kullanılıyor. - Windows: Zaten
WaitForSingleObjectAPI'si üzerinden olay tabanlı beklemeyi desteklediği için bir değişiklik yok.
3. Performans iyileştirmesi ve sonuçlar
Bu değişiklikle birlikte wait() çağrısı sırasında süreç, çekirdek açısından 'interruptible sleep' durumuna geçiyor. Yani CPU'yu hiç tüketmeden çekirdek alanında sessizce bekliyor ve süreç sonlanma sinyali geldiği anda hemen uyanıyor.
/usr/bin/time -v gibi araçlarla yapılan benchmark sonuçlarına göre, önceki yönteme kıyasla gereksiz context switching dramatik biçimde azaldı ve süreç sonlanmasını algılama hızı da anında iyileşti. Bu güncelleme hem psutil kütüphanesine hem de CPython çekirdeğine yansıtıldı; böylece Python geliştiricileri ek bir kod değişikliği yapmadan performans artışından yararlanabilecek.
Henüz yorum yok.