2 puan yazan GN⁺ 2024-10-03 | 1 yorum | WhatsApp'ta paylaş
  • Yüksek çekişme durumlarında Mutex uygulamaları arasındaki fark belirginleşir; Cosmopolitan Libc’nin pthread_mutex_t’si Windows ve Linux’taki başlıca uygulamalardan daha kısa çalışma süresi ve daha düşük CPU kullanımı gösteriyor
  • Windows’taki 24 çekirdekli Threadripper 29070WX testinde Cosmopolitan, Microsoft SRWLOCK’tan 2,75 kat daha hızlı ve 18 kat daha az CPU kaynağı kullanıyor
  • Linux’taki 96 çekirdekli Threadripper Pro 7995WX üzerinde glibc’den 3 kat, musl libc’den 11 kat daha hızlı; CPU zamanı farkı daha da açılıyor
  • MacOS M2 Ultra’da Apple Libc az farkla önde; Cosmopolitan ARM ortamında XNU’nun ulock sistem çağrısına dayanan basit bir algoritma kullanıyor
  • Performansın temeli Google’ın nsync entegrasyonu; CAS hızlı yolu, bekleyenler kuyruğu, futex/ulock/WaitOnAddress(), açlığı önleme ve designated waker tasarımı kilit unsurlar

Çekişmeli Mutex benchmark yöntemi

  • Test, 30 thread oluşturup her thread’in aynı global tamsayı g_chores değerini 100.000 kez artırması şeklinde yapıldı
  • Her artırma işlemi, pthread_mutex_lock() ile pthread_mutex_unlock() arasındaki çok küçük bir kritik bölgede yürütüldü
  • Ölçümler mikrosaniye cinsindedir ve üç farklı zaman türünü ayırır
    • wall time: Programın çalışması için geçen gerçek süredir; thread oluşturma ve join ek yükünü içerir
    • user time: Kullanıcı alanında harcanan CPU zamanı
    • system time: Çekirdekte harcanan CPU zamanı
  • Birden çok thread paralel çalıştığı için user time ve system time toplamı wall time’dan büyük olabilir
  • Çekişmesiz durumda uygulamalar arasındaki performans farkı genelde küçüktür; ancak çekişmeli durumda Mutex tasarımı farkları belirginleşir

Windows: SRWLOCK’tan hızlı Cosmopolitan

  • Windows testi 24 çekirdekli Threadripper 29070WX üzerinde yapıldı
  • Mark Waterman’ın MutexShootout projesi, yüksek çekişme senaryosunda Windows’un SRWLOCK yapısını en güçlü uygulama olarak değerlendirmişti
  • Aynı koşullarda Cosmopolitan pthread_mutex_t, SRWLOCK’tan daha kısa wall time ve daha düşük CPU kullanımı kaydetti
Uygulama wall time user time system time
Cosmopolitan pthread_mutex_t 148.940µs 328.125µs 62.500µs
Microsoft SRWLOCK 410.416µs 5.515.625µs 1.640.625µs
Microsoft CRITICAL_SECTION 949.187µs 7.937.500µs 5.078.125µs
MSVC 2022 std::mutex 991.750µs 12.156.250µs 4.031.250µs
spin lock 1.165.435µs 24.515.000µs 15.000µs
Cygwin pthread_mutex_t 9.780.803µs 1.937.000µs 6.156.000µs
  • Cosmopolitan Mutex, Microsoft SRWLOCK’tan 2,75 kat hızlı ve 18 kat daha az CPU kaynağı kullanıyor
  • Windows’a POSIX uygulaması sağlayan Cygwin Mutex ile karşılaştırıldığında 65 kat daha hızlı
  • Cygwin Mutex, bu kullanım senaryosunda spin lock’tan bile daha yavaş sonuç veriyor

Linux: CPU zamanı farkı wall time’dan daha büyük

  • Linux testi 96 çekirdekli Threadripper Pro 7995WX üzerinde yapıldı
Uygulama wall time user time system time
Cosmopolitan pthread_mutex_t 36.905µs 44.511µs 23.492µs
glibc pthread_mutex_t 101.353µs 150.706µs 2.724.851µs
spin lock 202.423µs 4.694.749µs 2.000µs
Musl libc pthread_mutex_t 411.013µs 2.167.898µs 9.926.850µs
  • Cosmopolitan Mutex, glibc’den 3 kat, musl libc’den 11 kat hızlı
  • CPU zamanı açısından glibc’den 42 kat, musl libc’den 178 kat daha az kullanıyor
  • Tüm thread’lerin seri hale getirilmiş bir işi yapmak zorunda olduğu iş yüklerinde Cosmopolitan, htop üzerinde yalnızca bir çekirdek aktifmiş gibi görünebilir
  • Aynı durumda glibc ve musl libc CPU kullanımını ciddi ölçüde doldurabilir; bu da aynı sunucuda birden fazla iş çalıştırırken yükü artırır

MacOS: Apple Libc az farkla önde

  • MacOS testi M2 Ultra üzerinde yapıldı
Uygulama wall time user time system time
Apple Libc 52.263µs 43.202µs 911.009µs
Cosmopolitan pthread_mutex_t 54.700µs 63.055µs 1.003.674µs
  • MacOS M2 ARM64’te Apple Libc, Cosmopolitan Mutex’ten biraz daha hızlı
  • Cosmopolitan’ın genel Mutex uygulaması bu platformda iyi çalışmıyor
  • MacOS ARM’de Cosmopolitan, Ulrich Drepper’ın Futexes Are Tricky yazısına dayanan daha basit bir algoritma kullanıyor
  • Bu yaklaşım ağır işlerin çoğunu XNU’nun ulock sistem çağrısına bırakıyor ve sonuçta Apple uygulamasıyla neredeyse aynı performansı veriyor

Performansın temeli: nsync entegrasyonu

  • Cosmopolitan Mutex performansının anahtarı Google’ın nsync kütüphanesinin entegrasyonu
  • nsync, GitHub’da 371 yıldızı olan ve Google’dan Mike Burrows tarafından yazılmış bir kütüphane
  • Cosmopolitan entegrasyonu sırasında şu çalışmalar yapıldı
    • nsync’in Mutex unlock fonksiyonunda uzun süredir fark edilmemiş bir hata bulundu ve düzeltildi
    • AARCH64’te C11 atomik işlemlerine port edilerek çekişmeli nsync Mutex’i upstream nsync’e göre %30 daha hızlı hale getirildi
    • futex benzeri sistem entegrasyonu yeniden yazılarak runtime taşınabilirliği mümkün kılındı
    • POSIX thread iptaliyle sorunsuz çalışması sağlandı

nsync nasıl çalışır

  • nsync, kilidi hızlı almak için başlangıçta hemen iyimser CAS(compare and swap) dener
  • Kilidi alamazsa çağıran thread’i bekleyenlerin çift bağlı listesine ekler
    • Her bekleyen, ayrı ve bağımsız bir cacheline üzerinde kendi semaforuna sahiptir
    • Bekleme durumuna giren thread artık ana kilide dokunmaz
    • Bu, birden çok çekirdeğin aynı cacheline’a dokunmasından doğan iletişim ek yükünü azaltmak için önemlidir
    • İlgili arka plan için Ulrich Drepper’ın What Every Programmer Should Know About Memory yazısına bağlantı veriliyor
  • nsync, thread’leri uyutmak için işletim sisteminin futex mekanizmasını kullanır
    • MacOS’ta futex’e ulock denir
    • Windows’ta WaitOnAddress() futex rolünü üstlenir
    • Cosmo’nun desteklediği işletim sistemleri içinde yalnızca NetBSD’de futex yoktur; POSIX semaforları kernel alanında uygulanır ve her semafor için yeni bir file descriptor gerekir
  • nsync, “long wait” kavramıyla açlığı (starvation) önler
    • Bir bekleyen 30 kez uyandırıldığı hâlde her seferinde dahili olarak kilidi almayı başaramazsa, henüz beklememiş thread’lerin kilidi almasını engellemek için kilide bir bit eklenir
    • Bu bit varsa bekleme kuyruğu bir ölçüde boşalana kadar yeni giren thread’in ilk CAS denemesi başarısız olur
  • Küçük kritik bölgelerde çekişen kullanım senaryoları designated waker kavramıyla hızlanır
    • Bir thread uyanıp kilidi almaya çalıştığında ana kilitte bir bit ayarlanır
    • nsync’te unlock fonksiyonu bir sonraki bekleyen thread’i uyandırma sorumluluğunu taşır
    • Bu bit sayesinde unlock yapan thread, zaten uyanık bir thread varken ikinci bir bekleyeni uyandırmak zorunda kalmaz
  • İlgili kaynak kodu cosmopolitan/third_party/nsync/mu.c ve cosmopolitan/libc/intrin/pthread_mutex_lock.c içinde bulunur

Gerçek servis ve doğrulama kodu

  • Cosmo Mutex kullanan canlı demo olarak http://ipv4.games/ sunucusu görülebilir
  • Bu servis 2 çekirdekli GCE VM üzerinde çalışıyor ve bugüne kadar en fazla 49.131.669 IP ölçeğinde botnet DDoS’a dayandı
  • nsync sayesinde SQL sorgularını arka plan thread’lerine taşıyıp thread’lerin birbirine mesaj gönderdiği bir yapı kullanılabildi
  • Durum göstergeleri /statusz üzerinden görülebilir
  • Benchmark kodu wall time’ı gettimeofday() ile, user time ve system time’ı getrusage() ile ölçer
  • Sonunda tüm artırma işlemlerinin yapıldığını doğrulamak için g_chores == THREADS * ITERATIONS kontrol edilir

Spin lock’a bakarken dikkat edilmesi gerekenler

  • Çekişmesiz durumda Mutex uygulamaları arasındaki fark küçüktür ve birkaç satırlık bir spin lock daha iyi olabilir
  • Ancak spin lock gerçekten başka seçenek olmadığında kullanılmalıdır
  • Kernel gibi son derece düşük seviyeli kısıtlar nedeniyle daha karmaşık yöntemlerin kullanılmasının zor olduğu yerlerde faydalıdır
  • nsync lock’un iç uygulama ayrıntısı olarak da spin lock kullanılabilir
  • Kilit performansına yalnızca wall time üzerinden bakınca spin lock iyi görünebileceği için, getrusage() ile CPU zamanı da birlikte kontrol edilmelidir

1 yorum

 
GN⁺ 2024-10-03
Hacker News yorumları
  • Yeni mutex uygulamaları ve karşılaştırmaları her zaman ilginçtir, ama bu benchmark yöntemi hoşuma gitmedi. Neredeyse bir mikrobenchmark gibi görünüyor.
    Hızlı lock’ları gerçekten dağıtıma alan kişiler genelde temel performans testi aracı olarak çok büyük, çok iş parçacıklı programları kullanır. Kritik bölüm uzunluğu, yarışan thread sayısı ve contention düzeyi değişen karmaşık iş yüklerinde, bir mutex’i hızlı ya da yavaş yapan etkenler değişiyor gibi görünüyor.
    Not olarak: WebKit’in hızlı lock’unu yazdım, lock uygulamaları için ParkingLot soyutlamasını icat ettim (Rust ve Unreal Engine’de de kullanılıyor) ve daha önce Java için hızlı lock araştırması ve makalesi de yaptım.

    • Masaüstü uygulama geliştirmiş biri olarak ekleyeyim: onlarca thread’in sık sık döndüğü uygulamalarda contention’ın yoğun olmadığı durumlara ait performans rakamlarını görmek isterim.
      Gerçek zamanlı ses programcısı olarak, zaten kilitli olmayan bir mutex’i almanın maliyeti benim için daha önemli. Bizim uygulamamızda bu durum açık ara en yaygın olanı. Benzer şekilde, N thread’in yarıştığı durum değil, başarısız olacak bir try-lock işleminin maliyetini de bilmek isterim.
      Cosmopolitan açık kaynak olduğuna göre bunu kendim de ölçebilirim, ama yine de eksik hissettiriyor.
    • Ben de aynı şeyi düşündüm. Mutex’in farklı türleri var ve belirli iş yüklerinde bazıları daha iyi oluyor. Aklıma DistributedMutex ve SharedMutex geliyor (https://github.com/facebook/folly/blob/main/folly/synchroniz..., https://github.com/facebook/folly/blob/main/folly/SharedMute...)
      Hash map’lerde olduğu gibi, tek bir hash map’in mümkün olan tüm iş yüklerinde daha iyi olması nadirdir.
    • Bu tarzdaki mutex, Python 3.13’ün PyMutex’inde de kullanılacak. PyMutex’in 3.13 öncesindeki PyThread_type_lock’tan ne kadar hızlı olduğunu gösteren gerçek bir benchmark var.
    • Kesinlikle bir mikrobenchmark ve genel performansı temsil etmeme olasılığı yüksek. Bu sayfa işletim sistemi benchmark uygulamaları için iyi bir çıta sunuyor. Yine de biraz daha akademi tarafına yönelik: https://gernot-heiser.org/benchmarking-crimes.html
    • O belirli benchmark, aksine istenmeyen davranışları, örneğin patolojik adaletsizliği, tercih ediyor olabilir. En iyi zamanlama, önce birinci thread’in tüm artırma işlemlerini, sonra ikinci thread’inkilerin tamamını çalıştırmak olur; çünkü bu şekilde işlemciler arası trafik en aza iner.
      Lock edinme başarısız olduğunda sabit bir süre (ör. 100µs) uyuyan bir mutex, neredeyse her zaman işleri kümelendirerek bu davranışa yaklaşır ve benchmark’ı “kazanabilir”. Ama gerçek uygulamalarda azıcık contention bile varsa böyle bir mutex berbat olur.
      Bu, söz konusu mutex kötü ya da pthread mutex iyi demek değil; ilgili mikrobenchmark’ın gerçek uygulama performansını öngörebilecek bir şeyi ölçmediği anlamına geliyor.
  • “Cosmopolitan Mutex’in iyi olmasının nedeni nsync adlı bir kütüphane kullanması” kısmına gelince, nsync’i ilk kez duyuyorum ama Mike Burrows Google’ın production mutex uygulamasını da yazmıştı: https://github.com/abseil/abseil-cpp/blob/master/absl/synchr...
    Bu yüzden bu mutex uygulamasının benchmark’a neden dahil edilmediğini merak ediyorum. Ayrıca macOS’ta __ulock’a delege ediyorsa, libc++’ın atomic kütüphanesindeki wait(), notify_one() üye fonksiyonlarını kullanarak aynı şeyi daha basit biçimde elde etmek mümkün gibi görünüyor.
    Daha önce Rust’ın mutex uygulamasını iyileştirmeyle ilgili büyük bir thread de vardı: https://github.com/rust-lang/rust/issues/93740#issuecomment-... İlginç olan, neredeyse tüm popüler mutex uygulamalarının iç işleyişinin ayrıntılı biçimde tartışılmış olması.

    • Ben AV’ye girdiğimde Mike zaten bir efsaneydi. Arama motorunun daha hızlı olması gerektiği her seferinde onun gelip birkaç çekirdek fonksiyonu yeniden yazdığı ve sonra eski işine döndüğü yönünde bir efsane vardı.
      Doğru olabilir, ama doğrudan doğrulayamam. Verimliliğe önem veren, aşırı zeki bir mühendisti. Yine de biz bir sunucuyu uzun süre çalışır halde tutmazdık.
    • Burrows, Burrows-Wheeler dönüşümü, Bigtable, Dapper, Chubby gibi işlerde de yer aldı.
    • O Rust thread’i eninde sonunda oraya varıyor, ama temelde Mara’nın çalışmasıyla ilgili ve bu yüzden onun Ocak 2023’te çıkan kitabından da söz ediliyor.
      Mevcut Rust mutex uygulaması bu yılın başında girdi; Linux’ta çok farklı olmayabilir, ama Windows ve Mac’te yeni çalışma olduğunu biliyorum.
      Yine de Mara’nın diğer uygulamaların içini anlattığı kısımlar hâlâ ilginç; ancak kendi durumunuzda bilginin eskimiş olup olmadığını kontrol etmek iyi olur.
    • Abseil’in mutex uygulamasının benchmark’ta yer almamasının nedeni, C değil bir C++ uygulaması olması olabilir. Sadece tahmin.
    • Mike Burrows sanırım bir ACM ödülü de aldı; orada fotoğrafı da görünüyor.
      https://awards.acm.org/award-recipients/burrows_9434147
  • “Henüz yeni bir C kütüphanesi olduğu için pürüzlü yerleri var ama o kadar hızlı iyileşiyor ki, prodüksiyonda kullanmamak mesleki sorumluluğu ihmal etmek gibi görünmeye başladı” cümlesi epey tuhaf. Cosmopolitan projesini takdir ediyorum ama bu tür abartılı üstünlük iddiaları genelde oldukça kötü bir uyarı işaretidir

    • Justine’in iddialarının genel olarak doğru olduğunu düşünüyorum. Yalnız abartılı ve kendini öne çıkaran ifadeler kullanmak onun tarzı, hatta belki kişiliği gibi görünüyor
      Bazılarına sert görünebileceğini de anlıyorum. Geçmişte llamacpp’de de bu şekilde bir dram yaşanmıştı
    • Justine oldukça yetenekli ve yaratıcı biri gibi görünüyor ama prodüksiyonda “yeni” ve “pürüzlü yerleri olan” bir libc kullanmak istemem
      Prodüksiyonda en öncelikli şey “inanılmaz hızlı iyileşmesi” değil, kararlılık, öngörülebilirlik ve güvenilirliktir. Elbette performans da önemlidir. Daha hızlı kod, altyapıyı azaltarak maliyet ve çevre açısından iyi olabilir. Ama hız son sıradadır
    • Uzun süre tek başına bilgisayar başında kod yazınca, belki de sosyal temas eksikliğinden belli ölçüde kibir de beraberinde geliyor gibi. Kişinin kendisini ya da yaptığı işin önemini dengeleyecek mekanizmalar yoksa, ortaya çıkan işler etkileyici olsa bile geniş çevrelerce kabul görmüş seviyenin ötesinde devasa görünebilir
      Örneğin APE bana çok etkileyici bir hack gibi geliyor ama “Yani artık yalnızca tek bir platformda güvensiz olmak yerine aynı anda birden çok platformda güvensiz mi olabiliyoruz?” diye eleştirmek de mümkün
      Teknoloji alanında ne kadar uzun süre kalırsanız, tamamen karşılıklı kazançların son derece nadir olduğunu; çoğu şeyin kazanç ve kayıpların birlikte geldiği bir trade-off olduğunu o kadar iyi anlıyorsunuz
    • En azından bana şaka gibi göründü
    • Sizinle Justine’in mizah anlayışının farklı olabileceğini hiç düşündünüz mü merak ediyorum. Bunu buraya koymanın kime faydası olduğunu düşündüğünüzü de bilmiyorum
  • Tamamen konu dışı ama bir oyun geliştiricisi olarak, tüm geliştirici derlemelerinde çok fazla debug işi yapan yavaş mutexleri sevmeye başladım. Debug adı/ID’si olan, sahibini izleyen, çekişmede harcanan zamanı profiler’a raporlayan, sahiplik değişimini de profiler’a bildiren türden
    Oyunlar eşzamanlılığı farklı yapılandırma eğiliminde ve kilitlerden kaçınma kalıpları da gelişti. Ama bu kalıpları kullanmak zor ve programcının yapıyı değiştirmesini gerektiriyor. Kodların çoğu “şimdilik buraya bir lock koyalım da milestone’u geçelim” diye başlıyor
    Hızlı lock’lar da öngörülemez biçimde yavaşlayabilir ve gerçek zaman garantiniz varsa bunu bozar. Ortalama olarak hızlı olabilirler ama kuyruk gecikmesi ortadan kalkmaz. “Oyunumuz takılıyor” sorununu izlemek için geri çağrılan kişi olmak istemiyorum ama genelde o kişi ben oluyorum
    Bu yüzden yavaş lock kullanmak daha iyi. Profiler’da kocaman kırmızı görünen lock’tan bahsediyorum. Darbe aldığını görürseniz refactor edip ortadan kaldırırsınız
    Bunun zor bir beklenti olduğunu biliyorum. AAA prodüksiyonda profiler kullanmayı bilen insan sayısı parmakla sayılacak kadar az. Birden fazla prodüksiyonda da hep böyleydi
    Sızlanma için kusura bakmayın ama hızlı eşzamanlılık temel öğeleri ve algoritma araştırmaları devam etsin isterim

    • Daha da konu dışına çıkarsam, Rust ile oyun geliştirmenin keyifli olmasının nedenlerinden biri de bu
      Oyunlarda mümkünse lock çekişmesini asla istemezsiniz ve çoğu durumda lock almanın gereksiz olduğunu kanıtlayabilirsiniz. Örneğin her frame aşamalara ayrılır ve belli bir paylaşılan kaynağa mutable erişim yalnızca belirli bir aşamada gerekir. render() öncesindeki update() ya da asset hot reload gibi
      Scoped thread’ler ve Rust’ın borrowing kurallarıyla, mutex’e hiç ihtiyaç kalmayacak şekilde yapılandırabilirsiniz; ileride kod değişip buna ihtiyaç doğduğu anda derleyicinin katı biçimde hata vereceğinden emin olabilirsiniz
      Mümkünse profiler’da spike görmektense compile error almak her zaman daha iyidir
    • Tamamen katılıyorum. Deadlock tespiti ya da iç durum kontrolleri gibi debugging özellikleri maliyetini kolayca çıkarır. Bir lock’u performansı etkileyecek kadar sık alıyorsanız tasarımı yeniden gözden geçirmelisiniz. Thread’ler arasında mutable state paylaşımından kaçınmak gerekir
  • Bir yandan Cosmo/APE/redbean ailesi gerçekten etkileyici görünüyor; ilgili yazıların yorumları da genel olarak olumlu ve kavramın kendisini çürüten pek bir şey yok. Ama öte yandan, başkalarının bunu kullandığına dair neredeyse hiç bir şey duymuyorum
    Herkes yaptığı işi geniş biçimde paylaşmıyor elbette, ama aradan yıllar geçtiyse birkaç proje retrospektifi yazısı görmüş olmayı beklerdim. Gördüğüm Cosmo/APE/redbean atıflarının tamamı Justine’in sitesinden çıkmıştı
    O yüzden merak ediyorum. Gizli bir tuzak mı var? Sonuç almak için kötü bir şeyler yapan bir araç mı? Derleyicileri ya da çalışma zamanlarını derinlemesine bilmediğim için anlayamadığım tom7 tarzı bir şaka ya da trolleme mi? Yoksa gerçekten henüz yaygınlaşmamış dahiyane araçlar mı?

    • APE, her an engellenebilecek ustaca bir numarayla çalışıyor ve OpenBSD’de gerçekten engellendi
      Çapraz platform yazılım geliştiren çoğu kişi, tüm platformlarda çalışan tek bir yürütülebilir dosya istemez; desteklediği her platformda doğru çalışan tek bir kod tabanı ister
      Bu açıdan bakınca, Go gibi CGO’dan kaçınırsanız tüm hedeflere çapraz derleme yapabildiğiniz diller keyiflidir. Ama APE’nin üç farklı biçimde çalıştırılabilme sihri gerçekten zekice olsa da sonsuza dek çalışacağına dair güven vermiyor; çoğu kişi için de pratik getirisi pek yok
      Her platformun kendi paketleme ve imzalama gereksinimleri olduğundan, platforma özgü hedefler için ayrı ayrı derlemek daha iyi
    • Kişisel olarak, cosmo ve ape çok zekice görünse de sıradan araçlar zaten iyi çalışıyorsa iş ortamında bu tür bir zekâya ihtiyacım yok
      Örneğin projenizi zaten başka işletim sistemleri ve platformlar için çapraz derleyebiliyorsanız ya da böyle bir derleme altyapınız varsa, her yerde çalışan tek bir ikili üretmek için çözüm aramanıza gerek yok
      Ayrıca APE, birden çok işletim sisteminde çalışmak için zekice hack’ler kullanıyor. Yürütülebilir dosya biçimleri evrildikçe o hack bir gün bozulursa ne olacak? Bu değişime göre APE’yi düzeltmeye kimsenin zamanı yoksa?
      Buna karşılık gcc, clang, go, rust gibi sıkıcı araçlar, güncellenip evrilen işletim sistemlerinde de çalışmaya devam edecek. Bu yüzden ben sıkıcı tarafta kalıyorum. Zekice olanla ilgilenmememin nedeni, sıkıcı olanın benim için gayet iyi çalışması
    • Mozilla’nın llamafile’ı bunu kullanıyor. Model ağırlıklarını ve yürütülebilir dosyayı tek bir pakette birleştirip cosmo/ape platformlarında her yerde çalıştırılabilir kılıyor; etkileşim için redbean HTTP sunucusu da başlatıyor
      Birleştirilmiş ağırlıklar olmadan çalıştırıp ağırlıkları dosya sisteminden okutmak da mümkün. Yerel bir LLM’i “indir ve hemen çalıştır” haline getirmenin en kolay yolu olabilir
    • Cosmopolitan bana hep eğlenceli blog yazıları çıkaran teknik bir açık gibi gelmiştir. Yaratıcılığı ve kurguya duyulan takıntısıyla HN gibi yerlerde ön sayfaya çıkması neredeyse garanti olan türden
      Ama libc gibi temel teknoloji olarak kullanmak için, daha çok eğlenceli oyuncaklara ya da küçük kişisel projelere yararlı görünüyor
      Bu bağlamda glibc, musl, msvcrt gibi şeylere ciddi bir alternatif olarak sunulduğunda biraz tuhaf geliyor. Çok sevimli bir hack, ama ciddi biçimde güvendiğim bir şeyin içinde keşfedersem epey şaşırırım
    • Mozilla’da Cosmopolitan libc tabanlı Llamafile projesi var: https://github.com/Mozilla-Ocho/llamafile
      Hugging Face’e de bu formatta yeniden paketlenmiş popüler modelleri düzenli olarak yüklüyorlar: https://huggingface.co/models?search=llamafile
      Ancak bunun küçük modelleri hızlıca denemenin ötesinde ne kadar pratik olduğu ayrı bir mesele
  • Madem bu kadar iyiyse, neden tüm C kütüphanelerinin aynı hileyi benimsemediğini merak ediyorum
    Tahminimce bu hileler muhtemelen yalnızca belirli mimarilerde, belirli CPU modellerinde, belirli iş yüklerinde veya erişim desenlerinde her zaman hızlıdır. Desteklenen tüm donanımlarda çeşitli iş yükleri doğru düzgün benchmark edilirse aynı fayda ortaya çıkmayabilir
    Ya da Cosmopolitan’ın uygulamaya çalıştığı pthread API semantiği ince bir şekilde farklıdır ve bu uygulama spesifikasyona sıkı sıkıya uymuyordur
    Birden fazla libc yazarının işletim sistemi temel öğeleriyle ilgili en güncel araştırmaları takip edemediğini hayal etmek zor

    • Bu tür projelerin tek bir belirli API dışında da onlarca önceliği var. Tek bir API’ye takılıp kalmak, sınırlı zamanı kullanmanın iyi bir yolu değil. Karşı örnek olarak Linux’un yaygın libc’lerindeki malloc ve string rutinlerine bakmak yeterli
      glibc’nin malloc’u idare eder, ama toplam hız ve ölçeklenebilirlikte daha modern alternatiflere kolayca yeniliyor. Parçalanma sorunu ağır ve zamanla kötüleşiyor; gerçek iş yüklerini ciddi etkileyen MALLOC_ARENA_MAX gibi pek çok ayar da var. musl malloc ise performans açısından her seviyede berbat. Çok iş parçacıklı bir programda musl ayırıcısını kullanmak performansı o kadar kötü bozuyordu ki neredeyse ihmal sayılabilirdi
      musl’de SIMD ile optimize edilmiş string karşılaştırma rutinleri gibi şeyler de yok. Önemsiz olmayan programlarda bu işler için ne kadar CPU döngüsü harcandığını bilseniz şaşırırsınız; gerçek profillerde de açıkça görünür ve bunu iyileştirmek neredeyse tüm programlara genel olarak fayda sağlar. glibc’nin optimize rutinleri iyi, ama yine de daha hızlı olabilecek gibi görünüyor
      Bunlar “tek bir mimariye özgü olup genelleştirilemeyen optimizasyonlar” değil. Özellikle bu iki alan, neredeyse tüm iş yüklerinde duvar saati süresini 2–5 kat azaltan ve uzun vadeli çalışma kümesi kullanımını da ciddi biçimde iyileştiren, iyi araştırılmış ve anlaşılmış alanlar. Peki neden benimsenmedi? Her zamanki gibi, muhtemelen yapılacak başka işler vardı ya da musl’de olduğu gibi en yüksek performans yerine basitliği önceleyen çakışan öncelikler vardı
      Bu projeleri suçlamıyorum. Kimse “programım berbat derecede yavaş ve hiçbir şeyi doğru düzgün yapamayacak şekilde tasarlandı, bununla gurur duyuyorum” demez. Ama o projelerde çalışanların tasarımda yalnızca kusursuz Pareto sınırını seçtiği fikri hiç gerçekçi değil ve çoğu projenin gerçekte nasıl yürüdüğünü yansıtmıyor
    • Siyaset, NIH sendromu ve eski bakımcılar yüzünden olabilir
      glibc’de ya da C++ tarafındaki eşdeğerinde bir şeyi değiştirmek sonsuza kadar sürüyor
      Senkronizasyon temel öğelerinin pek çok türü var; pthreads bunların yalnızca bir kısmını destekliyor. Kendinizi bununla sınırlarsanız, genel olarak taşınabilirlik karşılığında performanstan vazgeçmiş oluyorsunuz
    • “Birden fazla libc yazarının işletim sistemi temel öğeleriyle ilgili en güncel araştırmaları takip edemediğini hayal etmek zor” ifadesinin alaycı olup olmadığını merak ediyorum
      libc bakımcılarını bilmem, ama birkaç şeyi bakımını yapan biri olarak en son araştırmayı uygulamaya çalışmam. Kararlılığı korumaya ve performansın kabul edilebilir olduğundan emin olmaya çalışırım. Araştırma uygulaması benim “bakım” bütçemin dışında
    • pthread mutex uygulamasını değiştirmenin ABI değerlendirmeleri gerektirip gerektirmediğini merak ediyorum
    • “Madem bu kadar iyiyse, neden tüm C kütüphaneleri aynı hileyi benimsemedi?” sorusu bana şu fıkrayı hatırlatıyor
      Bir adam ve bir istatistikçi yolda yürürken 50 avroluk bir banknot görür. İstatistikçi yürümeye devam eder, adam durup “Bakın, yerde para var” der. Bunun üzerine istatistikçi “Sahte olmalı. Gerçek olsaydı biri çoktan almış olurdu” deyip yürümeye devam eder. Diğer adam parayı alır
  • Thread’ler ve mutex’ler bilgisayar biliminde işleri en karmaşık hâle getiren unsurlardır. Yeni bir uygulamaya, yıllarca büyük ölçekte kullanılana kadar her zaman şüpheyle bakarım
    Bu tür threading mekanizmalarındaki hatalar çoğu zaman en yoğun incelemelerden bile kaçar. 90’ların ortasında Java ortaya çıktığında Solaris’teki her türlü thread ve mutex hatası açığa çıkmıştı
    En hızlı mutex uygulamasına değil, güvenilir bir uygulamaya ihtiyacımız var

    • Mutex’ler en “karmaşık” şeyler olmaktan çok uzak. Verimli şekilde uygulamanın da çok fazla yolu yok. Çoğu durumda, özellikle okuma yolunda, onlardan kaçınmak en iyisi
  • Bu kod mutex kilidi performansını değil, mutex çekişmesini benchmark ediyor. Kilitleri bu şekilde kullanıyorsanız kodunuzu yeniden değerlendirmeniz gerekir
    Her thread g_chores değerini artırdığında mutex’i kilitleyip açıyor. Bu da mutex’i sık sık edinme ve bırakma overhead’i yaratıyor; thread başına 100.000 kez tekrarlanıyor
    Bu overhead, kilit mekanizmaları arasındaki gerçek performans farklarını maskeliyor. Çünkü benchmark gerçek işten değil, kilit çekişmesinden domine ediliyor. Bu tür benchmark’lar işe yaramaz

  • Justine’in ve yaptığı işlerin hayranıyım, ama bu muhtemelen mutex benchmark test vakası olarak en az ilginç olanlardan biri. Birden fazla thread’in aynı mutex’e sürekli yüklenmesi zaten baştan kaçınılması gereken bir durum
    Bu yüzden hangi mutex uygulamasının bu durumu en iyi ele aldığı bana pek ilginç gelmiyor

    • Mutex için iyi bir benchmark test vakası olarak ne düşündüğünü merak ediyorum
    • Kilit veya semafor kullandığım çoğu durumda bunlar çok pahalı bir kaynağın etrafında oluyor. O kaynağın kullanımı, kilidin performans overhead’ini gölgede bırakıyor
    • O hâlde ne ölçülmeli? Çekişme olmayan durum önemli ve bir temel çizgi oluşturuyor, ama onun dışında mutex’in zayıf noktası tam da burası. Çekişmeyi iyi yönetemezse donanım boşta kalır, scheduler işi artar veya kernel’e girişler çoğalır
      Önemli bir şeyi atladım: çekişme altında performansı kötü olan kilitler bellek ağında hotspot yaratmak gibi çok olumsuz sistemik etkilere neden olabilir ve bu da burada ortaya çıkar
    • “Birden fazla thread aynı mutex’e sürekli yüklenmemeli” değerlendirmesine tamamen katılmak zor
      Aynı mutex’e birden fazla thread’in yığıldığı birkaç durum aklıma geliyor. Basit bir örnek, liste veya dictionary gibi veri yapılarını eşzamanlı doldurma işi
      Bunu mesaj geçirme ile de yapabilirsiniz, ama daha fazla bellek kullanabilir ve paylaşılan bir konuma yazmak için beklemekten daha yavaş olabilir
  • Production hız, verimlilik ya da açıkça “zekice hack”lerle ilgili değildir
    Pazar sabaha karşı 3’te bozulan bir sistemi düzeltmek için çağrılmayacağımı garanti etmek adına verimliliğin %50’sinden feragat etmem gerekirse, her seferinde o seçimi yaparım
    Production güvenilirlikle ilgilidir ve güvenilir kod yazmak “hızlı” kod yazmaktan 10 kat daha zordur