- Linux çekirdeği, throughput ile yanıt süresi arasındaki ödünleşim için çeşitli preemption modlarını uzun süredir koruyor ve Peter Zijlstra’nın yeni yama setiyle tembel preemption (PREEMPT_LAZY) tartışması yeniden ciddi biçimde hız kazanıyor
- Mevcut
PREEMPT_NONE,PREEMPT_VOLUNTARY,PREEMPT_FULL,PREEMPT_RTmodları, preemption’a izin verilen kapsam bakımından farklılık gösteriyor; preemption ne kadar sık olursa yanıt verebilirlik o kadar artabiliyor, ancak bunun throughput ve lock rekabeti üzerinde maliyeti büyüyor PREEMPT_LAZY,TIF_NEED_RESCHED_LAZYbayrağıyla “yeniden zamanlama gerekli ama hemen değil” durumunu işaretliyor ve preemption’ın çoğunu zamanlayıcı tikine kadar erteliyor- Uzun vadede amaç, gerçek zamanlı olmayan preemption modlarını
PREEMPT_LAZYvePREEMPT_FULLile sınırlamak ve çekirdeğin çeşitli yerlerindekicond_resched()çağrılarının büyük bölümünü kaldırmak - Mevcut yama seti için hâlâ kararlılık, çağrı noktalarının gözden geçirilmesi ve performans testleri gerekiyor; ilk testlerde
PREEMPT_LAZYthroughput’uPREEMPT_VOLUNTARY’nin biraz gerisinde kalıyor
Linux çekirdeğinin mevcut preemption modları
- Mevcut çekirdek, çalışan bir görevin başka bir görev tarafından ne zaman preempt edilebileceğini kontrol eden çeşitli preemption modları sunuyor
PREEMPT_NONE: çalışan görevin yalnızca zaman dilimini tamamen kullandığında preempt edilmesine izin veren en basit modPREEMPT_VOLUNTARY: çekirdek içine gerektiğinde preempt edilebilecek çok sayıda nokta ekleyen modPREEMPT_FULL: spinlock tutulması gibi çekirdeğin engellediği bölümler dışında neredeyse her noktada preemption’a izin veren modPREEMPT_RT: çoğu şeyin önüne preemption’ı koyan ve spinlock tutan kodun büyük bölümünü de preempt edilebilir hâle getiren mod
- Preemption seviyesi yüksek olduğunda, fare hareketi ya da bir nükleer reaktörde yaklaşan anormallik sinyali gibi olaylara daha hızlı tepki verilebiliyor
- Ancak preemption sıklaştıkça, uzun süre çalışan CPU yoğun işlerin toplam throughput değeri düşebiliyor ve lock rekabeti artabiliyor
- Birçok dağıtım, çekirdeği
PREEMPT_DYNAMICsahte modu ile derliyor- Önyükleme sırasında yukarıdaki üç gerçek zamanlı olmayan moddan biri seçilebiliyor
- Varsayılan değer
PREEMPT_VOLUNTARY debugfsbağlanmış sistemlerde mevcut mod/sys/kernel/debug/sched/preemptüzerinden görülebiliyor
cond_resched() neden gerekliydi?
PREEMPT_NONEvePREEMPT_VOLUNTARY, çekirdek kodu çalışırken rastgele preemption’a izin vermiyor- Çekirdek içinde uzun işler sürdüğünde, en düşük gecikmenin öncelik olmadığı sistemlerde bile aşırı gecikme ortaya çıkabiliyor
- Bunu önlemek için uzun çalışan döngülerin çeşitli yerlerine
cond_resched()çağrıları eklendi- Her çağrı ek bir gönüllü preemption noktası
PREEMPT_NONEmodunda da çalışıyor- Çekirdekte bu tür yüzlerce çağrı var
- Bu yaklaşım, yalnızca geliştiricilerin yerleştirdiği noktalarda çalışan bir heuristic
- Gereksiz çağrılar olabilir
- Gerekli yerlere çağrı eklenmemiş olabilir
- Zamanlama kararı mantığının çekirdek kodunun tamamına yayılmasına yol açıyor
Tembel preemption’ın temel çalışma biçimi
- Çekirdek, mevcut görevin preempt edilip edilemeyeceğine karar verirken birden fazla değişkeni birlikte değerlendiriyor
- Bunlardan
TIF_NEED_RESCHED, daha yüksek öncelikli bir görevin CPU erişimi beklediğini gösteren bir bayrak- Daha yüksek öncelikli bir görev uyandığında, bu bayrak o anda çalışan göreve ayarlanabiliyor
- Bu bayrak yoksa çekirdeğin mevcut görevi preempt etmesine gerek kalmıyor
- Çekirdek çeşitli noktalarda
TIF_NEED_RESCHEDdeğerini kontrol ederek mevcut görevi preempt edebiliyor- Zamanlayıcının timer tick’i
- Sistem çağrısından sonra kullanıcı alanına dönerken
- Interrupt handler tamamlandığında
cond_resched()çağrısında
- Tembel preemption yaması yeni bir bayrak,
TIF_NEED_RESCHED_LAZY, ekliyor- Yeniden zamanlama gerekiyor ama mutlaka hemen yürütülmesi gerekmiyor anlamına geliyor
PREEMPT_LAZYmodunda olayların çoğuTIF_NEED_RESCHEDyerine bu yeni bayrağı ayarlıyor
- Çekirdekten kullanıcı alanına dönülen noktalarda, bu iki bayraktan yalnızca birinin ayarlı olması bile zamanlayıcının çağrılmasına yol açıyor
- Gönüllü preemption noktaları ve interrupt dönüş yollarında ise yalnızca
TIF_NEED_RESCHEDkontrol ediliyor
PREEMPT_LAZY’nin getirdiği ödünleşim
PREEMPT_LAZYmodunda, çekirdek içindeki çoğu olay mevcut görevi hemen preempt etmiyor- Bunun yerine timer tick işleyicisi
TIF_NEED_RESCHED_LAZYayarlı mı diye bakıyor- Ayarlıysa
TIF_NEED_RESCHEDde ayarlanıyor - Sonuç olarak çalışan görev preempt edilebiliyor
- Ayarlıysa
- Görevler, genellikle CPU’yu gönüllü olarak bırakmadıkları sürece, kendi zaman dilimlerine yakın bir süre çalışıyor
- Bunun iyi throughput sağlaması bekleniyor
- Bu değişiklikle
PREEMPT_LAZYdePREEMPT_FULLgibi, çekirdek preemption’ı neredeyse her zaman etkinmiş gibi çalışabiliyor- Preemption sayacı izin verdiği sürece her an preempt edilebiliyor
- Başka koşullar engel olmuyorsa uzun süre çalışan çekirdek kodu da preempt edilebiliyor
- Gerçekten anında preemption gereken durumlarda erteleme yapılmıyor
- Örneğin interrupt işleme sonucunda bir gerçek zamanlı görev çalışabilir hâle gelirse
TIF_NEED_RESCHEDayarlanıyor - Bu durumda timer tick beklenmeden neredeyse anında preemption gerçekleşiyor
- Örneğin interrupt işleme sonucunda bir gerçek zamanlı görev çalışabilir hâle gelirse
- Yalnızca
TIF_NEED_RESCHED_LAZYayarlıysa preemption olmuyor- Bu nedenle
PREEMPT_LAZYçekirdeği,PREEMPT_FULLçekirdeğine göre çalışan görevi preempt etme olasılığı çok daha düşük
- Bu nedenle
cond_resched() kaldırılana kadar kalan işler
- Uzun vadeli hedef, gerçek zamanlı olmayan preemption modlarını ikiye indirmek
PREEMPT_LAZYPREEMPT_FULL
PREEMPT_LAZY,PREEMPT_NONEilePREEMPT_VOLUNTARYarasında konumlanıyor ve bu ikisinin yerini alması planlanıyor- Preemption neredeyse her yerde mümkün hâle geldiğinde, belirli noktalara ayrıca gönüllü preemption noktaları ekleme ihtiyacı azalıyor
- Şu anda
cond_resched()çağrıları hâlâ duruyorPREEMPT_NONEvePREEMPT_VOLUNTARYvar oldukça bunlara ihtiyaç var- Ayrıca tembel preemption kararlı hâle getirilirken sorun çıkmamasına yardımcı oluyorlar
- Mevcut yama setinde
cond_resched()yalnızcaTIF_NEED_RESCHEDdeğerini kontrol ediyor- Bu yüzden
PREEMPT_VOLUNTARYya daPREEMPT_NONEaltında hemen preempt edilecek birçok durum artık gecikebiliyor
- Bu yüzden
- Steve Rostedt, özellikle
PREEMPT_VOLUNTARYiçincond_resched()eski anlamını korursa geçişin daha kolay olup olmayacağını sordu - Thomas Gleixner, yalnızca
TIF_NEED_RESCHEDkontrol edilmesi tercihinin doğru olduğunu düşünüyor- Çünkü bu, tüm
cond_resched()çağrılarının tek tek gözden geçirilmesini zorunlu kılıyor - Tembel bit kontrolü gerekmeyen çağrılar,
PREEMPT_LAZYuygulanırken kaldırılabilir - Tembel bit kontrolü gerektiren çağrılar ise kalmalı
- Çünkü bu, tüm
- Gleixner,
cond_resched()çağrılarının %5’inden azınınTIF_NEED_RESCHED_LAZYkontrolüne ihtiyaç duyacağını öngörüyor - Geçiş tamamlanmadan önce yüzlerce
cond_resched()çağrısının incelenmesi ve bunların çoğunun kaldırılması gerekiyor - Ankur Arora’nın ayrı bir yama seti, buna ilişkin bazı ayrıntıları ele alıyor
- Kapsamlı performans testleri de gerekli
- Mike Galbraith’in ilk testlerinde tembel preemption throughput’u
PREEMPT_VOLUNTARY’nin biraz altında kaldı
- Mike Galbraith’in ilk testlerinde tembel preemption throughput’u
Nihai hedef
- Tembel preemption çalışmasının sonucunda çekirdek biraz daha küçük ve basit hâle gelebilir
- Amaç, zamanlayıcıyla ilgili çağrıları kod tabanının her yanına serpiştirmeden öngörülebilir gecikme süreleri sunan bir çekirdek elde etmek
- Mevcut yaklaşım daha iyi bir çözüm gibi görünüyor, ancak o noktaya ulaşmak için hâlâ zamana ihtiyaç var
1 yorum
Hacker News yorumları
Umut verici görünüyor. EEVDF gibi mevcut durumu sadeleştirirken iyileştiren bir yön olduğu için bundan daha iyisi zor olurdu.
Öncelik alma düzeyinin neden küresel bir mod değil de belirli bir olayın özelliği olmadığını merak ediyorum. Bazı olayların diğerlerinden daha düşük gecikmeyle işlenmesi gerekir.
Dolayısıyla bir olayın sahip olabileceği en yüksek öncelik bile, programın bağlam değişimi yaşamadan önce alabileceği zaman diliminin ne kadar kısa olduğuyla sınırlıdır. Herhangi bir tür olaya düşük gecikmeyle güvenilir biçimde yanıt vermek için, CPU yoğun tüm programların o olay ne kadar nadir olursa olsun her zaman bir performans bedeli ödemesi gerekir.
Olası öncelik alma noktaları zamanlayıcının bir özelliğidir ve burada küresel mod olarak tartışılan da budur. Öncelik alma noktaları arttıkça elbette bir sürecin uygunsuz bir anda önceliğinin alınma olasılığı artar; ama aynı zamanda öncelikleri doğru yansıtma fırsatı da artar. Soruda bahsedilen öncelik alma düzeyi, yani zamanlayıcının verdiği öncelik, gerçekten de sürecin bir özelliğidir ve ayarlanabilir. Linux’un varsayılan zamanlayıcısı da önceliği olan süreçlere daha fazla zaman dilimi vermeye ve diğer süreçlerin önceliğini daha az almaya çalışır.
SCHED_IDLE, SCHED_BATCH, SCHED_NORMAL/OTHER gecikmeli öncelik alma kullanıyor; FIFO, RR, DEADLINE ise mevcut Full davranışını kullanıyor.
Bu yüzden çalışan uygulama sayısını en aza indirmek ya da çoğu kullanıcının yaşadığı kısa anlar için elle kontrol etmek önemli. CPU yoğun işler de gerçek anlamda verimli kaynak kullanımından ziyade kötü kod olma ihtimalini daha sık taşıyabilir. Oyunlarda performansa öncelik vermek gerekir; ama çoklu görev için sistemi durma noktasına getirmemek gibi hassas bir denge gerekir. Her hâlükârda bu esasen boşta kalan işler için olduğundan, kullanıcının bir betikten çeşitli davranışları açıp kapatabileceği basit bir komut vermekten daha fazla otomasyona pek gerek yok gibi görünüyor.
“Mevcut çekirdekte bir görevin başka bir görev için ne zaman önceliğinin alınabileceğini ayarlayan dört mod var” deniyor; bunun çekirdek görevleri için mi, yoksa kullanıcı görevlerini de kapsayıp kapsamadığını merak ediyorum.
Yamanın gönderildiği bağlantılı dizide sayıları bulamadım. Bu değişikliğin pratik potansiyelini gösterecek ilk benchmarkların artık yapılmış olmasını beklerdim, merak ediyorum.
Kapsamlı performans testlerinin gerektiği, Mike Galbraith’in ilk çalışmaya başladığı ve gecikmeli öncelik almanın iş hacminin PREEMPT_VOLUNTARY’den biraz düşük olduğunu gösteren sonuçlar sunduğu söyleniyor.
Zamanlayıcının çekirdeğin geri kalan koduyla ne kadar sıkı bağlı olduğunu merak ediyorum.
Örneğin öncelik almayı hiç önemsemeyen bilimsel hesaplama uygulamaları için zamanlayıcıyı ciddi ölçüde sadeleştirmek istesek, bunu temiz ve modüler bir şekilde yapmak mümkün olur mu? Gerçek bir faydası olur mu?
tasksetile doğrudan oraya koymak en güçlü yöntemdir.Ancak o zaman işleri gerçekten elle CPU’lara atamanız gerekir ve tüm işlerin yanlış CPU’ya düşmesi de kolayca yaşanabilir. Standart yöntem, kesme maskelerini ayarlayıp kesmelerin “iş” CPU’suna gitmesini engellemek ve belirli cgroup’ların yalnızca verilen cpuset içinde çalışmasını sağlamak için cpuset kullanmaktır.
Çalışma listesi çok kısalacağı için zamanlayıcı ne yaparsa yapsın etkisi oldukça küçük olur. Uygulama çok fazla G/Ç yapmıyorsa kesme de fazla olmaz. Tickless çekirdek kullanabiliyorsanız — bugün hâlâ ayrı bir seçenek mi yoksa varsayılan mı bilmiyorum — uzun süreler boyunca neredeyse hiç kesme olmayabilir.
Ancak ciddi ölçüde sadeleştirme isteğinin nedeni hatalardan kaçınmak olur; iyi ayarlanmış varsayılan zamanlayıcıya kıyasla elde edilecek performans çok fazla değildir. Çok sayıda ayar var ama o tarafta da fazla hata yoktu. Safça sadeleştirme çoğunlukla performans kazandırmak yerine kaybettirir. Etkileşimsiz bir sistem çalıştırıyorsanız en kolay değişiklik süreç zaman kotasını artırmaktır.