C ++ 11'de std :: atomic :: Compare_exchange_weak () 'i anlama


88
bool compare_exchange_weak (T& expected, T val, ..);

compare_exchange_weak(), C ++ 11'de sağlanan karşılaştırma-değişim ilkelerinden biridir. Bu var zayıf o nesnenin değeri eşit olsa bile false döndüren anlamında expected. Bunun nedeni, uygulamak için bir dizi talimatın (x86'daki gibi değil) kullanıldığı bazı platformlardaki sahte başarısızlıktır . Bu tür platformlarda, bağlam anahtarı, aynı adresin (veya önbellek satırının) başka bir iş parçacığı tarafından yeniden yüklenmesi vb. İlkelde başarısız olabilir. Bu var spuriousbu (eşit olmayan nesnesinin değeri değil gibi expectedişlem başarısız). Bunun yerine, bir tür zamanlama sorunları.

Ama beni şaşırtan şey, C ++ 11 Standardında (ISO / IEC 14882) söylenenler,

29.6.5 .. Sahte başarısızlığın bir sonucu, zayıf karşılaştırma ve değiş tokuşun neredeyse tüm kullanımlarının bir döngü içinde olmasıdır.

Neredeyse tüm kullanımlarda neden bir döngü içinde olması gerekiyor ? Bu, sahte başarısızlıklar nedeniyle başarısız olduğunda döngü yapacağımız anlamına mı geliyor? Eğer durum buysa, neden compare_exchange_weak()döngüyü kendimiz yazıp kullanmaya zahmet ediyoruz ? Bizim compare_exchange_strong()için sahte başarısızlıklardan kurtulmamız gerektiğini düşündüğüm bir şeyi kullanabiliriz . Yaygın kullanım durumları compare_exchange_weak()nelerdir?

İlgili başka bir soru. "C ++ Eşzamanlılık Eylemde" adlı kitabında Anthony,

//Because compare_exchange_weak() can fail spuriously, it must typically
//be used in a loop:

bool expected=false;
extern atomic<bool> b; // set somewhere else
while(!b.compare_exchange_weak(expected,true) && !expected);

//In this case, you keep looping as long as expected is still false,
//indicating that the compare_exchange_weak() call failed spuriously.

!expectedDöngü koşulunda neden var? Bütün konuların aç kalmasını ve bir süre ilerleme kaydedememesini önlemek için var mı?

Düzenleme: (son bir soru)

Tek bir donanım CAS talimatının olmadığı platformlarda, hem zayıf hem de güçlü sürüm LL / SC (ARM, PowerPC, vb. Gibi) kullanılarak uygulanır. Öyleyse, aşağıdaki iki döngü arasında herhangi bir fark var mı? Varsa neden? (Bana göre benzer performansa sahip olmaları gerekir.)

// use LL/SC (or CAS on x86) and ignore/loop on spurious failures
while (!compare_exchange_weak(..))
{ .. }

// use LL/SC (or CAS on x86) and ignore/loop on spurious failures
while (!compare_exchange_strong(..)) 
{ .. }

Bu son soruyla geliyorum, hepiniz bir döngü içinde performans farkı olabileceğini söylüyorsunuz. Ayrıca C ++ 11 Standardında (ISO / IEC 14882) bahsedilmektedir:

Karşılaştırma ve değiştirme bir döngü içindeyken, zayıf sürüm bazı platformlarda daha iyi performans sağlar.

Ancak yukarıda analiz edildiği gibi, bir döngüdeki iki sürüm aynı / benzer performansı vermelidir. Özlediğim şey nedir?


4
İlk soruyla birlikte, çoğu durumda yine de döngü yapmanız gerekir (ister güçlü ister zayıf sürümü kullanın) ve zayıf sürüm, güçlü olandan daha iyi performansa sahip olabilir.
TC

2
Hem zayıf hem de güçlü CAS, hem baloncuklu sıralama hem de hızlı sıralama "takas kullanılarak" uygulandığı gibi "LL / SC kullanılarak" uygulanır; yani, görevi tamamlamak için kullanılan ilkel işlem budur. Ne sarmak etrafında LL / SC çok farklıdır. Zayıf CAS yalnızca LL / SC'dir. Güçlü CAS, bir sürü başka şey içeren LL / SC'dir.
Sneftel


@TuXiaomi bu bağlantıdaki cevaba sahipse, Standartta belirtildiği gibi "zayıf sürümün bazı platformlarda daha iyi performans göstermesinin" nedenini anlayamıyorum.
Deqing

@Deqing Diğerlerinde, Compare_exchange_weak, diğer işlemcilerin veya iş parçacığının kesintileri veya eylemleri nedeniyle sahte bir şekilde başarısız olabilir. Bu platformlarda, Compare_exchange_strong etkin bir şekilde Compare_exchange_weak için bir döngüdür - eğer sahte bir şekilde başarısız olursa tekrar döngü oluşturur. Yardımcı olur mu? Belki yanılıyorum
Tu Xiaomi

Yanıtlar:


75

Neden bir döngü içinde değiş tokuş yapılıyor?

Genellikle, ilerlemeden önce işinizin yapılmasını istersiniz, bu nedenle, compare_exchange_weakbaşarılı olana kadar değiş tokuş yapmaya çalışması için bir döngüye koyarsınız (yani geri gelir true).

Ayrıca compare_exchange_strongbir döngüde sıklıkla kullanıldığını unutmayın . Sahte hata nedeniyle başarısız olmaz, ancak eşzamanlı yazmalar nedeniyle başarısız olur.

weakBunun yerine neden kullanılmalı strong?

Oldukça kolay: Sahte başarısızlık sık görülmez, bu nedenle performans açısından büyük bir darbe değildir. Buna karşılık, böyle bir başarısızlığı tolere weaketmek strong, bazı platformlarda sürümün çok daha verimli bir şekilde uygulanmasına izin verir (buna kıyasla ): strongher zaman sahte hata olup olmadığını kontrol etmeli ve onu maskelemelidir. Bu pahalı.

Bu nedenle, bazı platformlardan weakçok daha hızlı olduğu strongiçin kullanılır.

Ne zaman weakve ne zaman kullanmalısınız strong?

Referans kullanılacak ipuçları devletler weakkullanılacak ve strong:

Karşılaştırma ve değiştirme bir döngü içindeyken, zayıf sürüm bazı platformlarda daha iyi performans sağlar. Zayıf bir karşılaştırma ve değiştirme bir döngü gerektirdiğinde ve güçlü bir döngü gerektirmediğinde, güçlü olan tercih edilir.

Öyleyse cevabı hatırlamak oldukça basit görünüyor: Eğer sadece sahte başarısızlık yüzünden bir döngü başlatmanız gerekiyorsa, bunu yapmayın; kullanın strong. Yine de bir döngünüz varsa, kullanın weak.

Neden !expectedörnekte var

Duruma ve istenen anlam bilgisine bağlıdır, ancak genellikle doğruluk için gerekli değildir. Bunu ihmal etmek çok benzer bir anlambilim ortaya çıkarır. Yalnızca başka bir iş parçacığının değeri olarak sıfırlayabileceği bir durumda false, anlambilim biraz farklı olabilir (yine de bunu isteyebileceğiniz anlamlı bir örnek bulamıyorum). Ayrıntılı bir açıklama için Tony D.'nin yorumuna bakın.

Başka bir iş parçacığı yazdığında bu sadece hızlı bir izdir true: Sonra truetekrar yazmaya çalışmak yerine iptal ederiz .

Son sorunuz hakkında

Ancak yukarıda analiz edildiği gibi, bir döngüdeki iki sürüm aynı / benzer performansı vermelidir. Özlediğim şey nedir?

Gönderen Vikipedi :

LL / SC'nin gerçek uygulamaları, söz konusu bellek konumunda eşzamanlı güncelleme yoksa her zaman başarılı olmaz. Bir bağlam anahtarı, başka bir yükleme bağlantısı veya hatta (birçok platformda) başka bir yükleme veya depolama işlemi gibi iki işlem arasındaki herhangi bir istisnai olay, mağaza koşulunun sahte bir şekilde başarısız olmasına neden olacaktır. Bellek veri yolu üzerinden yayınlanan herhangi bir güncelleme varsa daha eski uygulamalar başarısız olacaktır.

Dolayısıyla, LL / SC, örneğin bağlam anahtarında sahte olarak başarısız olacaktır. Şimdi, güçlü sürüm, bu sahte arızayı tespit etmek ve yeniden deneyerek onu maskelemek için "kendi küçük döngüsünü" getirecektir. Bu kendi döngüsünün aynı zamanda normal bir CAS döngüsünden daha karmaşık olduğunu unutmayın, çünkü sahte başarısızlık (ve onu maskelemek) ile eşzamanlı erişimden kaynaklanan başarısızlık (bu, bir değerle sonuçlanır) arasında ayrım yapmak zorundadır false. Zayıf versiyonun böyle bir kendi döngüsü yoktur.

Her iki örnekte de açık bir döngü sağladığınız için, güçlü sürüm için küçük bir döngüye sahip olmak gerekli değildir. Sonuç olarak, strongsürümle ilgili örnekte , hata kontrolü iki kez yapılır; bir kez compare_exchange_strong(sahte başarısızlık ve eşzamanlı erişimi ayırt etmesi gerektiğinden daha karmaşıktır) ve bir kez de döngünüz tarafından. Bu pahalı çek gereksizdir ve neden weakburada daha hızlı olacaktır.

Ayrıca argümanınızın (LL / SC) bunu uygulamak için yalnızca bir olasılık olduğunu unutmayın. Hatta farklı komut setlerine sahip daha fazla platform var. Ek olarak (ve daha da önemlisi), olası tüm veri türleristd::atomic için tüm işlemleri desteklemesi gerektiğini unutmayın; bu nedenle, on milyon baytlık bir yapı bildirseniz bile, bunu kullanabilirsiniz . CAS'a sahip bir CPU'da bile, on milyon bayt CAS yapamazsınız, bu nedenle derleyici başka talimatlar üretecektir (muhtemelen edinmeyi kilitleyecektir, ardından atomik olmayan bir karşılaştırma ve takas, ardından bir kilit açma). Şimdi, on milyon baytı değiştirirken kaç şeyin olabileceğini düşünün. Dolayısıyla, 8 baytlık değişim için sahte bir hata çok nadir olsa da, bu durumda daha yaygın olabilir.compare_exchange

Kısacası, C ++ size iki anlambilim verir, bir "en iyi çaba" ( weak) ve "Aralarında kaç kötü şey olursa olsun, kesinlikle yapacağım" ( strong). Bunların çeşitli veri türlerinde ve platformlarda nasıl uygulandığı tamamen farklı bir konudur. Zihinsel modelinizi kendi platformunuzdaki uygulamaya bağlamayın; standart kitaplık, farkında olabileceğinizden daha fazla mimariyle çalışmak üzere tasarlanmıştır. Çıkarabileceğimiz tek genel sonuç, başarıyı garanti etmenin genellikle sadece denemekten ve olası başarısızlıklara yer bırakmadan daha zor olduğu (ve bu nedenle ek çalışma gerektirebileceğidir).


"Güçlü kullanın, ancak hiçbir şekilde sahte başarısızlığa tahammül edemezseniz." - Eşzamanlı yazma ve sahte başarısızlıktan kaynaklanan arızalar arasında gerçekten bir ayrım yapan bir algoritma var mı? Aklıma gelenler, bazen güncellemeleri kaçırmamıza izin veriyor ya da yapmıyor, bu durumda yine de bir döngüye ihtiyacımız var.
Voo

4
@Voo: Yanıt güncellendi. Şimdi referanstan ipuçları dahil edilmiştir. Bir ayrım yapan bir algoritma olabilir. Örneğin, "birinin güncellenmesi gerekir" anlamını düşünün: Bir şeyi güncellemek tam olarak bir kez yapılmalıdır, yani eşzamanlı yazma nedeniyle başarısız olduğumuzda, başka birinin yaptığını biliriz ve iptal edebiliriz. Sahte başarısızlık nedeniyle başarısız olursak, kimse onu güncellemedi, bu yüzden yeniden denemeliyiz.
gexicide

8
" Örnekte neden! Bekleniyor? Doğruluk için gerekli değildir. Bunu atlamak aynı semantiği verir." - öyle değil ... eğer ilk değişimin bulduğu biçin başarısız olduğunu söylerse true, o zaman - expectedşimdi ile true- && !expecteddöngü olmadan başka bir (aptalca) değişim dener trueve truebu whiledöngüden önemsiz bir şekilde "başarılı" olabilir , ancak sergileyebilir eğer bbu arada geri dönmüş olsaydı anlamlı şekilde farklı davranış false, bu durumda döngü devam eder ve nihayetinde kırılmadan önce yeniden kurulabilir . b true
Tony Delroy

@TonyD: Doğru bunu açıklığa kavuşturmalıyım.
gexicide

Üzgünüm çocuklar, son bir soru daha ekledim;)
Eric Z

18

Çeşitli çevrimiçi kaynaklardan (örn. Bu ve bu ), C ++ 11 Standardından ve burada verilen cevaplardan geçtikten sonra bunu kendim cevaplamaya çalışıyorum .

İlgili sorular birleştirilir (örneğin, " neden! Bekleniyor? ", "Neden Compar_exchange_weak () döngüye koyulsun? " İle birleştirilir ve cevaplar buna göre verilir.


Compar_exchange_weak () neden neredeyse tüm kullanımlarda bir döngü içinde olmak zorunda?

Tipik Desen A

Atomik değişkendeki değere göre bir atomik güncellemeye ihtiyacınız var. Bir hata, değişkenin istediğimiz değerle güncellenmediğini ve onu yeniden denemek istediğimizi gösterir. Eşzamanlı yazma veya sahte başarısızlık nedeniyle başarısız olup olmadığını gerçekten umursamadığımızı unutmayın . Ama bunu bakım yapmak bizi olduğu bu değişikliği yapmak.

expected = current.load();
do desired = function(expected);
while (!current.compare_exchange_weak(expected, desired));

Gerçek dünya örneği, birkaç iş parçacığının aynı anda tekil bağlantılı bir listeye bir öğe eklemesidir. Her iş parçacığı ilk önce baş işaretçisini yükler, yeni bir düğüm atar ve başlığı bu yeni düğüme ekler. Son olarak, yeni düğümü kafa ile değiştirmeye çalışır.

Başka bir örnek, mutex'i std::atomic<bool>. En çok bir evre ilk set hangi iplik bağlı bir anda kritik bölüm girebilirsiniz currentiçin trueve döngü çıkın.

Tipik Desen B

Bu aslında Anthony'nin kitabında bahsedilen modeldir. A modelinin aksine , atom değişkeninin bir kez güncellenmesini istersiniz, ancak bunu kimin yaptığı umurunuzda değildir. Güncellenmediği sürece tekrar deneyin. Bu genellikle boole değişkenleriyle kullanılır. Örneğin, bir durum makinesinin ilerlemesi için bir tetikleyici uygulamanız gerekir. Hangi iplik tetiği çektiğine bakılmaksızın.

expected = false;
// !expected: if expected is set to true by another thread, it's done!
// Otherwise, it fails spuriously and we should try again.
while (!current.compare_exchange_weak(expected, true) && !expected);

Bir muteksi uygulamak için genellikle bu kalıbı kullanamayacağımızı unutmayın. Aksi takdirde, birden fazla iş parçacığı aynı anda kritik bölümün içinde olabilir.

Bununla birlikte, compare_exchange_weak()bir döngünün dışında kullanılması nadirdir . Aksine, güçlü versiyonun kullanımda olduğu durumlar vardır. Örneğin,

bool criticalSection_tryEnter(lock)
{
  bool flag = false;
  return lock.compare_exchange_strong(flag, true);
}

compare_exchange_weak burada uygun değildir çünkü sahte başarısızlık nedeniyle geri döndüğünde, muhtemelen kritik bölümü henüz kimse işgal etmemiştir.

Konu Açlıktan mı?

Bahsetmeye değer bir nokta, sahte başarısızlıklar olmaya devam ederse ve böylece iş parçacığı aç bırakılırsa ne olacağıdır. Teorik compare_exchange_XXX()olarak, bir dizi talimat olarak uygulandığında platformlarda gerçekleşebilir (örneğin, LL / SC). LL ve SC arasındaki aynı önbellek hattına sık erişim, sürekli sahte hatalara neden olacaktır. Daha gerçekçi bir örnek, tüm eşzamanlı iş parçacıklarının aşağıdaki şekilde araya eklendiği aptal bir zamanlamadan kaynaklanmaktadır.

Time
 |  thread 1 (LL)
 |  thread 2 (LL)
 |  thread 1 (compare, SC), fails spuriously due to thread 2's LL
 |  thread 1 (LL)
 |  thread 2 (compare, SC), fails spuriously due to thread 1's LL
 |  thread 2 (LL)
 v  ..

Olabilir mi?

Neyse ki, C ++ 11'in gerektirdiği sayesinde sonsuza kadar olmayacak:

Uygulamalar, zayıf karşılaştırma ve değiştirme işlemlerinin, atom nesnesi beklenenden farklı bir değere sahip olmadığı veya atom nesnesinde eşzamanlı değişiklikler olmadığı sürece tutarlı bir şekilde yanlış döndürmemesini sağlamalıdır.

Neden Compare_exchange_weak () kullanıp döngüyü kendimiz yazıyoruz? Karşılaştırma_exchange_strong () kullanabiliriz.

Değişir.

Durum 1: Her ikisinin de bir döngü içinde kullanılması gerektiğinde. C ++ 11 diyor ki:

Karşılaştırma ve değiştirme bir döngü içindeyken, zayıf sürüm bazı platformlarda daha iyi performans sağlar.

X86'da (en azından şu anda. Belki bir gün daha fazla çekirdek tanıtıldığında performans için LL / SC gibi benzer bir şemaya başvurur), zayıf ve güçlü sürüm esasen aynıdır çünkü ikisi de tek bir talimata indirgenir cmpxchg. Atomik olarakcompare_exchange_XXX() uygulanmayan diğer bazı platformlarda (burada tek bir donanım ilkelinin olmadığı anlamına gelir), güçlü olanın sahte arızaları ele alması ve buna göre yeniden denemesi gerekeceğinden, döngü içindeki zayıf sürüm savaşı kazanabilir.

Fakat,

Nadiren, biz tercih edebilirsiniz compare_exchange_strong()üzerinde compare_exchange_weak()bile bir döngü içinde. Örneğin, atomik değişken yüklenir ve hesaplanan yeni bir değer değiştirilir (yukarıya bakın function()) arasında yapılacak çok şey olduğunda . Atomik değişkenin kendisi sık sık değişmiyorsa, her sahte başarısızlık için maliyetli hesaplamayı tekrar etmemize gerek yoktur. Bunun yerine, bu compare_exchange_strong()tür hataları "absorbe etmeyi " umabiliriz ve hesaplamayı yalnızca gerçek bir değer değişikliği nedeniyle başarısız olduğunda tekrar ederiz.

Durum 2: Yalnızca compare_exchange_weak() bir döngü içinde kullanılması gerektiğinde. C ++ 11 ayrıca şunu da söylüyor:

Zayıf bir karşılaştırma ve değiştirme bir döngü gerektirdiğinde ve güçlü bir döngü gerektirmediğinde, güçlü olan tercih edilir.

Bu genellikle, zayıf sürümden kaynaklanan sahte hataları ortadan kaldırmak için döngü oluşturduğunuzda geçerlidir. Eşzamanlı yazma nedeniyle değişim başarılı veya başarısız olana kadar yeniden denersiniz.

expected = false;
// !expected: if it fails spuriously, we should try again.
while (!current.compare_exchange_weak(expected, true) && !expected);

En iyi ihtimalle, tekerlekleri yeniden icat etmek ve aynı performansı sağlamaktır compare_exchange_strong(). Daha da kötüsü? Bu yaklaşım, donanımda sahte olmayan karşılaştırma ve değiştirme sağlayan makinelerden tam olarak yararlanmada başarısız olur .

Son olarak, başka şeyler için döngü yaparsanız (örneğin, yukarıdaki "Tipik A Örneği" ne bakın), o zaman compare_exchange_strong()bizi önceki duruma geri götüren bir döngüye de konulacak iyi bir şans vardır .


17

Neredeyse tüm kullanımlarda neden bir döngü içinde olması gerekiyor ?

Çünkü döngü yapmazsanız ve bu sahte bir şekilde başarısız olursa, programınız yararlı bir şey yapmamış demektir - atomik nesneyi güncellemediniz ve mevcut değerinin ne olduğunu bilmiyorsunuz (Düzeltme: Aşağıdaki Cameron açıklamasına bakın). Arama yararlı bir şey yapmazsa, bunu yapmanın anlamı nedir?

Bu, sahte başarısızlıklar nedeniyle başarısız olduğunda döngü yapacağımız anlamına mı geliyor?

Evet.

Eğer durum buysa, neden compare_exchange_weak()döngüyü kendimiz yazıp kullanmaya zahmet ediyoruz ? Bizim için sahte hatalardan kurtulması gerektiğini düşündüğüm Compare_exchange_strong () kullanabiliriz. Compare_exchange_weak () 'ın yaygın kullanım durumları nelerdir?

Bazı mimarilerde compare_exchange_weakdaha verimlidir ve sahte hatalar oldukça nadir olmalıdır, bu nedenle zayıf formu ve bir döngüyü kullanarak daha verimli algoritmalar yazmak mümkün olabilir.

Genel olarak, sahte hatalar konusunda endişelenmenize gerek olmadığından, algoritmanızın döngü yapması gerekmiyorsa, bunun yerine güçlü sürümü kullanmak muhtemelen daha iyidir. Güçlü sürüm için bile yine de döngü yapması gerekiyorsa (ve birçok algoritmanın yine de döngü yapması gerekiyorsa), zayıf formu kullanmak bazı platformlarda daha verimli olabilir.

!expectedDöngü koşulunda neden var?

Değer truebaşka bir iş parçacığı tarafından ayarlanmış olabilir , bu nedenle onu ayarlamaya çalışırken döngüye devam etmek istemezsiniz.

Düzenle:

Ancak yukarıda analiz edildiği gibi, bir döngüdeki iki sürüm aynı / benzer performansı vermelidir. Özlediğim şey nedir?

Elbette, sahte arızanın mümkün olduğu platformlarda, sahte arızayı compare_exchange_strongkontrol etmek ve yeniden denemek için uygulamanın daha karmaşık olması gerektiği açıktır .

Zayıf form sahte başarısızlık üzerine geri döner, yeniden denemez.


2
+1 Tüm sayılarda gerçekte doğru (Q'nun çaresizce ihtiyacı olan).
Tony Delroy

Hakkında you don't know what its current value isbir sahte arıza gerçekleştiğinde 1 noktasında, mevcut değeri, bu anda beklenen değer ile aynı olmamalıdır? Aksi takdirde, gerçek bir başarısızlık olur.
Eric Z

IMO, hem zayıf hem de güçlü sürüm, tek bir CAS donanımı ilkelinin olmadığı platformlarda LL / SC kullanılarak uygulanır. Öyleyse bana göre neden while(!compare_exchange_weak(..))ve arasında performans farkı var while(!compare_exchange_strong(..))?
Eric Z

Üzgünüm çocuklar, son bir soru daha ekledim.
Eric Z

1
@Jonathan: Just bir nitpick, ancak bunu o spuriously başarısız olursa akım değerini bilmek (tabii o size değişkeni satırları okurken akım değeri tamamen başka bir sorun olup hala olsun, ama ne olursa olsun zayıf / güçlü bir var). Bunu, örneğin, değerinin boş olduğunu varsayarak bir değişkeni ayarlamaya çalışmak için kullandım ve başarısız olursa (sahte veya değil) denemeye devam et, ancak yalnızca gerçek değerin ne olduğuna bağlı.
Cameron

13

Pekala, atomik sola kaydırmayı gerçekleştiren bir işleve ihtiyacım var. İşlemcimin bunun için yerel bir işlemi yok ve standart kitaplığın bunun için bir işlevi yok, bu yüzden kendi işlemimi yazıyorum gibi görünüyor. İşte:

void atomicLeftShift(std::atomic<int>* var, int shiftBy)
{
    do {
        int oldVal = std::atomic_load(var);
        int newVal = oldVal << shiftBy;
    } while(!std::compare_exchange_weak(oldVal, newVal));
}

Şimdi, döngünün birden fazla çalıştırılmasının iki nedeni var.

  1. Sol vardiyamı yaparken başka biri değişkeni değiştirdi. Benim hesaplamamın sonuçları atomik değişkene uygulanmamalıdır, çünkü başka birinin yazdıklarını etkili bir şekilde silecektir.
  2. CPU'm geğirdi ve zayıf CAS sahte bir şekilde başarısız oldu.

Gerçekten hangisi olduğu umrumda değil. Sola kaydırma yeterince hızlı ki, başarısızlık sahte olsa bile, yine de yapabilirim.

Daha az hızlı olan ise, güçlü CAS'ın güçlü olmak için zayıf CAS'ı sarması gereken ekstra koddur. Zayıf CAS başarılı olduğunda bu kod fazla bir şey yapmaz ... ancak başarısız olduğunda, güçlü CAS'ın Durum 1 mi yoksa Durum 2 mi olduğunu belirlemek için bazı dedektiflik çalışmaları yapması gerekir. Bu dedektiflik çalışması ikinci bir döngü şeklini alır, kendi döngümün içinde etkili bir şekilde. İki iç içe döngü. Algoritma öğretmeninizin şu anda size baktığını hayal edin.

Ve daha önce de bahsettiğim gibi, o dedektiflik çalışmasının sonucu umurumda değil! Her iki durumda da CAS'ı yeniden yapacağım. Dolayısıyla, güçlü CAS kullanmak bana kesinlikle hiçbir şey kazandırmıyor ve bana küçük ama ölçülebilir bir verimlilik miktarı kaybettiriyor.

Başka bir deyişle, atomik güncelleme işlemlerini gerçekleştirmek için zayıf CAS kullanılır. CAS'ın sonucunu önemsediğinizde güçlü CAS kullanılır.


0

Sanırım yukarıdaki yanıtların çoğu "sahte başarısızlık" ı bir tür sorun olarak ele alıyor, performansa karşı doğruluk ödünleşimi.

Zayıf sürüm çoğu zaman daha hızlı olduğu için görülebilir, ancak sahte hata durumunda daha yavaş hale gelir. Ve güçlü versiyon, sahte hata olasılığı olmayan bir versiyondur, ancak neredeyse her zaman daha yavaştır.

Benim için temel fark, bu iki sürümün ABA sorununu nasıl ele aldığıdır:

zayıf sürüm ancak yükleme ve depolama arasındaki önbellek hattına kimse dokunmadıysa başarılı olur, bu nedenle ABA sorununu% 100 algılar.

güçlü sürüm yalnızca karşılaştırma başarısız olursa başarısız olur, bu nedenle fazladan önlemler alınmadan ABA sorununu algılamaz.

Yani teorik olarak, zayıf sıralı mimaride zayıf sürüm kullanıyorsanız, ABA tespit mekanizmasına ihtiyacınız yoktur ve uygulama çok daha basit olacak ve daha iyi performans sağlayacaktır.

Ancak x86'da (güçlü sıralı mimari), zayıf sürüm ve güçlü sürüm aynıdır ve her ikisi de ABA sorunundan muzdariptir.

Dolayısıyla, tamamen çapraz platformlu bir algoritma yazarsanız, yine de ABA sorununu çözmeniz gerekir, bu nedenle zayıf sürümü kullanmanın performans avantajı yoktur, ancak sahte arızaların üstesinden gelmek için bir performans cezası vardır.

Sonuç olarak - taşınabilirlik ve performans nedenleriyle, güçlü sürüm her zaman daha iyi veya eşit bir seçenektir.

Zayıf sürüm, ancak ABA karşı önlemlerini tamamen atlamanıza izin veriyorsa veya algoritmanız ABA'yı önemsemiyorsa daha iyi bir seçenek olabilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.