Mevcut "kilitsiz" uygulamalar çoğu zaman aynı modeli izler:
- bir durumu okuyun ve bir kopyasını alın *
- kopyayı değiştir *
- kilitli bir işlem yapmak
- başarısız olursa tekrar dene
(* isteğe bağlı: veri yapısına / algoritmaya bağlıdır)
Son parça ürkütücü bir şekilde spinlock'a benzer. Aslında, temel bir spinlock'tur . :)
Bu konuda @nobugz ile aynı fikirdeyim: Kilitsiz çoklu iş parçacığı işleminde kullanılan birbirine bağlı işlemlerin maliyetinde, gerçekleştirmesi gereken önbellek ve bellek tutarlılığı görevleri hakimdir .
Bununla birlikte, "kilitsiz" bir veri yapısıyla elde ettiğiniz şey, "kilitlerinizin" çok ince taneli olmasıdır. . Bu, iki eşzamanlı iş parçacığının aynı "kilide" (bellek konumu) erişme şansını azaltır.
Çoğu zaman işin püf noktası, adanmış kilitlerinizin olmamasıdır - bunun yerine, örneğin bir dizideki tüm öğeleri veya bağlantılı bir listedeki tüm düğümleri bir "döndürme kilidi" olarak ele alırsınız. Son okumanızdan bu yana güncelleme yoksa okur, değiştirir ve güncellemeye çalışırsınız. Varsa, yeniden deneyin.
Bu, ek bellek veya kaynak gereksinimleri gerektirmeden "kilitlemenizi" (üzgünüm, kilitlemesiz :) çok ince taneli hale getirir.
Daha ince taneli hale getirilmesi bekleme olasılığını azaltır. Ek kaynak gereksinimleri getirmeden olabildiğince ince ayar yapmak kulağa harika geliyor, değil mi?
Ancak eğlencenin çoğu, doğru yükleme / mağaza siparişi vermekten kaynaklanabilir .
Kişinin sezgilerinin aksine, CPU'lar bellek okuma / yazma işlemlerini yeniden sıralamakta özgürdür - bu arada çok akıllıdırlar: Bunu tek bir iş parçacığından gözlemlemekte zorlanacaksınız. Bununla birlikte, birden çok çekirdekte çoklu iş parçacığı oluşturmaya başladığınızda sorunlarla karşılaşacaksınız. Sezgileriniz çökecek: bir talimat kodunuzda daha önce olduğu için, aslında daha önce gerçekleşeceği anlamına gelmez. CPU'lar talimatları sıra dışı işleyebilirler ve bunu özellikle bellek erişimiyle ilgili talimatlar için, ana bellek gecikmesini gizlemek ve önbelleklerinden daha iyi yararlanmak için yapmaktan hoşlanırlar.
Şimdi, sezgiye karşı, bir kod dizisinin "yukarıdan aşağıya" akmadığı, bunun yerine hiç bir sekans yokmuş gibi çalıştığı ve "şeytanın oyun alanı" olarak adlandırılabileceği kesindir. Hangi yükleme / mağaza siparişlerinin gerçekleşeceği konusunda kesin bir cevap vermenin mümkün olmadığına inanıyorum. Bunun yerine, kişi her zaman mayıslar , mights ve teneke kutular açısından konuşur ve en kötüsüne hazırlanır. "Ah, CPU olabilir bu noktada, burada bir bellek bariyer koymak en iyisidir, böylece o yazma önce gelip bu okuma yeniden düzenlemek."
Bu olasılıklar ve güçlükler bile CPU mimarileri arasında farklılık gösterebildiği için meseleler karmaşıktır . Bu olabilir , örneğin, yani bir şey durum olmaz garanti bir mimaride meydana gelebilecek diğerine.
Doğru "kilitsiz" çoklu iş parçacığı elde etmek için, bellek modellerini anlamanız gerekir.
Bellek modelini ve garantileri doğru almak önemsiz değildir, ancak bu hikayedeMFENCE
gösterildiği gibi , Intel ve AMD, JVM geliştiricileri arasında bazı karışıklıklara neden olmak için belgelerde bazı düzeltmeler yaptı . Görünüşe göre, geliştiricilerin başından beri güvendikleri dokümantasyon ilk etapta o kadar kesin değildi.
Örtük bellek bariyer içinde .NET sonucu Kilitler, çoğu zaman olduğunu ... örneğin bkz (bunları kullanarak güvenli nedenle bu Joe Duffy - Brad Abrams - Vance Morrison büyüklüğünün tembel başlatma, kilitler, uçucuların ve bellek engeller. :) (O sayfadaki bağlantıları takip ettiğinizden emin olun.)
Ek bir bonus olarak, bir yan görevde .NET bellek modeliyle tanışacaksınız . :)
Ayrıca Vance Morrison'dan bir "eski ama goldie" var: Çok İş Parçacıklı Uygulamalar Hakkında Her Dev'in Bilmesi Gerekenler .
... ve elbette, @Eric'in bahsettiği gibi, Joe Duffy konu hakkında kesin bir okuma.
İyi bir STM, ince ayarlı kilitlemeye olabildiğince yaklaşabilir ve muhtemelen el yapımı bir uygulamaya yakın veya buna eşit bir performans sağlayacaktır. Bunlardan biri olan STM.NET gelen DevLabs projelerin MS.
Yalnızca .NET fanatiği değilseniz, Doug Lea JSR-166'da harika işler çıkardı .
Cliff Click , kilit şeritlemeye dayanmayan - Java ve .NET eşzamanlı karma tablolarının yaptığı gibi - hash tablolarına ilginç bir yaklaşım getiriyor ve 750 CPU'ya kadar iyi ölçekleniyor gibi görünüyor.
Linux bölgesine girmekten korkmuyorsanız, aşağıdaki makale mevcut bellek mimarilerinin iç yapıları ve önbellek hattı paylaşımının performansı nasıl bozabileceği hakkında daha fazla bilgi sağlar: Her programcının bellek hakkında bilmesi gerekenler .
@Ben, MPI hakkında birçok yorum yaptı: MPI'nin bazı alanlarda parlayabileceğine içtenlikle katılıyorum. MPI tabanlı bir çözüm, akıllı olmaya çalışan yarı pişmiş bir kilitleme uygulamasından daha kolay akıl yürütebilir, daha kolay uygulanabilir ve daha az hataya açık olabilir. (Bununla birlikte - öznel olarak - STM tabanlı bir çözüm için de geçerlidir.) Pek çok başarılı örneğin öne sürdüğü gibi, örneğin Erlang'da düzgün dağıtılmış bir uygulamayı doğru bir şekilde yazmanın ışık yılı daha kolay olduğuna da bahse girerim .
Ancak MPI, tek, çok çekirdekli bir sistemde çalıştırıldığında kendi maliyetlerine ve kendi sorunlarına sahiptir . Örneğin, Erlang'da, süreç planlaması ve mesaj kuyruklarının senkronizasyonu etrafında çözülmesi gereken sorunlar vardır .
Ayrıca, özünde, MPI sistemleri genellikle "hafif süreçler" için bir tür işbirliğine dayalı N: M çizelgeleme uygular. Bu, örneğin, hafif süreçler arasında kaçınılmaz bir bağlam değişikliği olduğu anlamına gelir. Bunun bir "klasik bağlam anahtarı" değil, çoğunlukla bir kullanıcı alanı işlemi olduğu doğrudur ve hızlı yapılabilir - ancak içtenlikle , kilitli bir işlemin gerektirdiği 20-200 döngü altına getirilebileceğinden içtenlikle şüpheliyim . Kullanıcı modu bağlam değiştirme kesinlikle daha yavaştırIntel McRT kitaplığında bile. N: Hafif süreçlerle M planlama yeni değil. LWP'ler uzun süre Solaris'teydi. Terk edildiler. NT'de lifler vardı. Artık çoğunlukla bir kalıntılar. NetBSD'de "aktivasyonlar" vardı. Terk edildiler. Linux, N: M iş parçacığı konusunda kendi görüşüne sahipti. Şimdiye kadar biraz ölmüş gibi görünüyor.
Zaman zaman yeni yarışmacılar vardır: örneğin Intel'den McRT veya en son Microsoft'tan ConCRT ile birlikte Kullanıcı Modu Planlama .
En düşük seviyede, bir N: M MPI programlayıcının yaptığını yaparlar. Erlang - veya herhangi bir MPI sistemi - yeni UMS'den yararlanarak SMP sistemlerinde büyük fayda sağlayabilir .
Sanırım OP'nin sorusu, herhangi bir çözüme yönelik / aleyhindeki öznel argümanlar ve esası ile ilgili değil, ancak buna cevap vermek zorunda kalırsam, sanırım bu göreve bağlıdır: tek bir sistem ile birçok çekirdek , ya düşük kilitleme / "kilit-serbest" teknikleri veya STM yukarıdaki kırışıklıklar gidermesinden bile, performans açısından en iyi sonuçlar verecektir ve muhtemelen epeyce bir MPI çözümüdür her zaman döverdi örneğin Erlang'da.
Tek bir sistem üzerinde çalışan orta derecede daha karmaşık herhangi bir şey oluşturmak için, belki klasik kaba taneli kilitlemeyi veya performans çok önemliyse bir STM'yi seçerdim.
Dağıtılmış bir sistem kurmak için, bir MPI sistemi muhtemelen doğal bir seçim olacaktır.
için
de MPI uygulamaları olduğunu unutmayın.NET de (etkin görünmese de).