Python Sleep fonksiyonu istenenden fazla duraksıyor

Ahmet Çavdar

Hectopat
Katılım
15 Mayıs 2020
Mesajlar
456
Çözümler
2
Daha fazla  
Sistem Özellikleri
Acer Aspire A715-75G - İ5 10300H 2.5 GHz Turbo 4.5 GHZ - Intel UHD Graphics & GTX 1650 - 16 GB 2933 MHz



Asus TUF Gaming FX-505DT BQ180T - R5 3550H 2.1 GHz Turbo 3.7 GHz - Vega 8 & GTX 1650 - 16 GB 2400 MHz
Cinsiyet
Erkek
Meslek
Bilgisayar Mühendisliği Öğrencisi
Merhaba. Dual boot bir bilgisayar kullanıyorum. Hem Windows 10 sistemimde hem de Ubuntu 20.04 sistemimde Python 3.8.10 sürümü kurulu. Aşağıdaki basit kodla iki sistemde de test yaptım.

Python:
ctr=0
start=time.time()

while time.time()-start<1:
 ctr+=1
 time.sleep(0.001)

print(ctr)

Ubuntu sistemimde 980 civarı çıktılar alıyorum ki bu gayet normal. Ancak Windows'ta tuhaf bir şekilde 40-65 arası çıktılar alıyorum. Arka planda çalışan programlar vb. yavaşlatabilecek şeyleri elimden geldiğince kapatmama rağmen böyle oluyor. Donanım olarak da yetersiz bir sistem kullanmıyorum, i5 10. nesil bir işlemci ve 2933MHz 16 GB bir RAM kullanmaktayım, Windows 10 çalıştırmaya ve bu basit göreve fazlasıyla yeterli diye tahmin ediyorum.

İşin tuhafı, Python 3.12.3 sürümü ile de deneme yapınca çıktılar 630 civarında oluyor. 3.8'den iyi ama gene de fazlasıyla düşük geliyor bana.

Sizce bu durumun sebebi ne olabilir?
 
Son düzenleme:
Daha önce farklı bir konu da bu durumu açıklamıştım. Windows real time bir OS değil. O zaman C#'ta timer için konuşmuştum, şimdi Python için açıklayayım. High resolution timer kullanmadıkça Windows'ta her bir tick yaklaşık olarak 16 ms sürüyor. O da 1 saniye de yaklaşık olarak 60 demek. İşlem yükü arttıkça işlemci de, tick geldiğinde yapmak için bekliyor olabilir.
Dediğin normal şartlarda doğru fakat Windows gerçek zamanlı bir işletim sistemi değil ve çok küçük zamanlayıcı uğraşlarında sapıtabiliyor.

Aşağıda paylaştığım Stackoverflow konusunda bahsi geçen durum timer'ın belirlenenin üzerinde bir süre sonra çalışıyor olması. Bunun sebebi yukarıda dediğim gibi Windows gerçek zamanlı bir işletim sistemi değil ve next tick geldiğinde işlemi yapıyor. Dolayısıyla doğru sonuçlar vermiyor aşırı küçük intervallarda. Eğer çok uzun süreler koyarsan, yaptığı gecikme göze batmıyor. Atıyorum 5 saniyede bir arttırmak gibi vs vs şeyler göze batmıyor ancak her mikro saniyede bir saydırmaya çalışamıyor.

En altta paylaştığım microsoft dökümanlarını incelersek Windows'un her yaklaşık 15 ms'te bir tick yenilediğini görürüz. Bu da 1 interval verdiğimiz şeyin saniyede 1000 kere değil, 66-67~ kere çalışıyor olması demek. Fakat yazılımın yaptığı işlemlerle falan filan bu çalışma süresi 60'a kadar düşebiliyor.

Ben doğruya en yakın sonucu 60'da buldum. 16.6 ms civarı yapıyor. 15 ms tick + 1 ms interval + 0.5 ms işlem gecikmesi = 16.5 ms şeklinde hesapladım.

Eki Görüntüle 2039415
Çeviri; Örneğin Windows x86 bir işlemcide çalışıyor, varsayılan her bir sistem saat tiki aralığı tipik olarak yaklaşık 15 milisaniye, ve bu saat tiklerinin minimum aralığı yaklaşık 1 milisaniye.

Devamında da default timer resolution ile 15 ms'te bir kontrol edebiliriz, high timer resolution ile 1 ms'te bir.
 
Daha önce farklı bir konu da bu durumu açıklamıştım. Windows real time bir OS değil. O zaman C#'ta timer için konuşmuştum, şimdi Python için açıklayayım. High resolution timer kullanmadıkça Windows'ta her bir tick yaklaşık olarak 16 ms sürüyor. O da 1 saniye de yaklaşık olarak 60 demek. İşlem yükü arttıkça işlemci de, tick geldiğinde yapmak için bekliyor olabilir.

Anlıyorum, açıklamanız için çok teşekkür ederim. İnternetten tick diye bahsettiğiniz şeyi araştırdığımda basit devrelerdeki clock sinyali benzeri bir tanım gördüm. Ve hem bu araştırmamdan hem de sizin yazdıklarınızdan anladığım kadarıyla zaman temelli işlemler bu sinyale bağlı. Python da dökümanında sleep fonksiyonunun yüksek keskinlikli zamanlayıcı sayesinde Windows 8.1 ve üzerinde 100 nanosaniye keskinliğinde işlem yapabildiğinden bahsetmiş ama sanırım bu mevzu son sürümlerde kullanılmaya başlanmış, 3.8 için durum ortada.

Anlamadığım şeyse şu, madem Python da dökümanında yazdığı gibi yüksek keskinlikteki zamanlayıcıyı kullanıyor, nasıl oluyor da 630 ile sınırlı kalıyor?
 
Anlamadığım şeyse şu, madem Python da dökümanında yazdığı gibi yüksek keskinlikteki zamanlayıcıyı kullanıyor, nasıl oluyor da 630 ile sınırlı kalıyor?
Hala bir de işlem gecikmesi var. Her yeni tick geldiğinde sıra senin işlemine gelmeli çalışmak için. Sıra geldikten sonra tekrar çalışmak bir sonraki ticki bekleyecek. Arada geçen tickleri umursamayacak. Bu varsayım tabii. Python arkada neler yapıyor onu bile bilmiyoruz.
 
Teşekkürler yardımlarınız için
 
Cok hassas islem yapman gerekiyorsa busy wait yapmayi dusunebilirsin.
Python:
import time

ctr = 0
start = time.time()


def now_ms() -> float:
    return time.time() * 1000


def busy_wait_sec(sec: float):
    end_ms = (now_ms()) + (sec * 1000)
    while now_ms() <= end_ms:
        pass


while time.time() - start < 1:
    ctr += 1
    # time.sleep(0.001)
    busy_wait_sec(0.001)

print(ctr)

1000'e cok yakin degerler elde edersin fakat bu hareketin CPU da gereksiz yuk olusturacagini unutma ama, her seyin bir bedeli var :)
 
Son düzenleme:
Aslında karşılaştığım sorun bu değildi. Saniyede 180-200 arası paketi UDP üzerinden aktarması gereken bir kod yazıyordum ve veri ile senkron olabilmesi için 0.002 gibi küçük bir süre için sleep fonksiyonunu kullanmaya niyetlendim. Veri gönderimi 60-65 paketle sınırlandı. Ben de acaba sorun nerede diye ararken sleep fonksiyonunun sebep olup olmadığını anlamak için bu basit testi yazdım.

Busy wait konseptini ilk kez gördüm ama dediğiniz gibi, işlemciye yük bindirmesi sıkıntı bir durum. Aslında bahsettiğim mevzuda hem veri ile senkronluğu sağlamak için hem de yeni veri gelmediği takdirde eski veriyi basarak hem alıcıda hem vericide gereksiz yere işlem yükü oluşturmamak için sleep kullanmaya niyetlendim.

Öneriniz için teşekkür ederim, çok hassas zamanlamaya ihtiyaç duyarsam kullanabileceğim yeni bir yöntem öğrenmiş oldum.
 

Geri
Yukarı