En Hızlı Mutex’ler
(justine.lol)- 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_choresdeğerini 100.000 kez artırması şeklinde yapıldı - Her artırma işlemi,
pthread_mutex_lock()ilepthread_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.cvecosmopolitan/libc/intrin/pthread_mutex_lock.ciç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 * ITERATIONSkontrol 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
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.
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-lockişleminin maliyetini de bilmek isterim.Cosmopolitan açık kaynak olduğuna göre bunu kendim de ölçebilirim, ama yine de eksik hissettiriyor.
Hash map’lerde olduğu gibi, tek bir hash map’in mümkün olan tüm iş yüklerinde daha iyi olması nadirdir.
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üphanesindekiwait(),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ı.
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.
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.
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
Bazılarına sert görünebileceğini de anlıyorum. Geçmişte llamacpp’de de bu şekilde bir dram yaşanmıştı
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
Ö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
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
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()öncesindekiupdate()ya da asset hot reload gibiScoped 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
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ı?
Ç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
Ö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ı
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
Ama
libcgibi temel teknoloji olarak kullanmak için, daha çok eğlenceli oyuncaklara ya da küçük kişisel projelere yararlı görünüyorBu bağlamda
glibc,musl,msvcrtgibi ş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ımHugging 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
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_MAXgibi 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ılabilirdimusl’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
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
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
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
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_choresdeğ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ıyorBu 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
Ö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
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