Aracılığıyla paylaş


Eşitleme ve Çok İşlemcili Sorunlar

Uygulamalar, yalnızca tek işlemcili sistemlerde geçerli olan varsayımlar nedeniyle çok işlemcili sistemlerde çalıştırıldığında sorunlarla karşılaşabilir.

İş Parçacığı Öncelikleri

Biri diğerinden daha yüksek önceliğe sahip iki iş parçacığına sahip bir program düşünün. Tek işlemcili bir sistemde, zamanlayıcı daha yüksek öncelikli iş parçacıklarını tercih ettiğinden, yüksek öncelikli iş parçacığı denetimi düşük öncelikli iş parçacığına geri göndermez. Çok işlemcili bir sistemde her iki iş parçacığı da her biri kendi işlemcisinde aynı anda çalışabilir.

Uygulamalar, yarış koşullarından kaçınmak için veri yapılarına erişimi eşitlemelidir. Daha yüksek öncelikli iş parçacıklarının düşük öncelikli iş parçacıklarının girişimi olmadan çalıştığını varsayar kod, çok işlemcili sistemlerde başarısız olur.

Bellek Sıralama

bir işlemci bir bellek konumuna yazdığında, performansı geliştirmek için değer önbelleğe alınır. Benzer şekilde, işlemci performansı geliştirmek için önbellekten okuma isteklerini karşılamaya çalışır. Ayrıca işlemciler, uygulama tarafından istenmeden önce bellekten değer getirmeye başlar. Bu, kurgusal yürütmenin bir parçası olarak veya önbellek satırı sorunlarından kaynaklanabilir.

CPU önbellekleri paralel olarak erişilebilen bankalara bölünebilir. Bu, bellek işlemlerinin sıra dışı olarak tamamlandığı anlamına gelir. Bellek işlemlerinin sırayla tamamlandığından emin olmak için çoğu işlemci bellek engeli yönergeleri sağlar. tam bellek engeli bellek engeli yönergesi öncesinde görünen bellek okuma ve yazma işlemlerinin bellek engeli yönergesi sonrasında görünen bellek okuma ve yazma işlemlerinden önce belleğe işlenmesini sağlar. okuma bellek engeli yalnızca bellek okuma işlemlerini ve yazma bellek engelini sıralar yalnızca bellek yazma işlemlerini sıralar. Bu yönergeler ayrıca derleyicinin engeller arasında bellek işlemlerini yeniden sıralayan iyileştirmeleri devre dışı bırakmasını sağlar.

İşlemciler alma, serbest bırakma ve çit semantiği ile bellek engelleri için yönergeleri destekleyebilir. Bu semantikler, bir işlemin sonuçlarının hangi sırayla kullanılabilir hale geldiğini açıklar. Alma semantiği ile, işlemin sonuçları kodda ondan sonra görünen herhangi bir işlemin sonuçlarından önce kullanılabilir. Yayın semantiği ile işlemin sonuçları, kodda ondan önce görünen herhangi bir işlemin sonuçlarından sonra kullanılabilir. Çit semantiği, alma ve serbest bırakma semantiğini birleştirir. Çit semantiğine sahip bir işlemin sonuçları, kodda ondan sonra görünen işlemlerden önce ve ondan önce görünen işlemlerden sonra kullanılabilir.

SSE2'yi destekleyen x86 ve x64 işlemcilerde yönergeler mfence (bellek çiti), lfence (yük çiti) ve sfence (depolama çiti) şeklindedir. ARM işlemcilerde, yetkisiz erişimler dmb ve dsb. Daha fazla bilgi için işlemci belgelerine bakın.

Aşağıdaki eşitleme işlevleri, bellek sıralamasını sağlamak için uygun engelleri kullanır:

  • Kritik bölümlere giren veya ayrılan işlevler
  • SRW kilitlerini edinen veya bırakan işlevler
  • Tek seferlik başlatma başlangıç ve tamamlama
  • EnterSynchronizationBarrier işlevini
  • Eşitleme nesnelerini işaretleyen işlevler
  • Bekleme işlevleri
  • Birbirine bağlı işlevler (NoFence soneki olan işlevler veya _nf soneki olan iç işlevler hariç)

Yarış Koşulunu Düzeltme

İlk kez CacheComputedValue yürüten işlemci ana belleğe iValue yazmadan önce ana belleğe fValueHasBeenComputed yazabileceğinden, aşağıdaki kod çok işlemcili sistemlerde bir yarış koşuluna sahiptir. Sonuç olarak, aynı anda FetchComputedValue yürüten ikinci bir işlemci TRUEolarak fValueHasBeenComputed okur, ancak iValue yeni değeri hala ilk işlemcinin önbelleğindedir ve belleğe yazılmamıştır.

int iValue;
BOOL fValueHasBeenComputed = FALSE;
extern int ComputeValue();

void CacheComputedValue()
{
  if (!fValueHasBeenComputed) 
  {
    iValue = ComputeValue();
    fValueHasBeenComputed = TRUE;
  }
}
 
BOOL FetchComputedValue(int *piResult)
{
  if (fValueHasBeenComputed) 
  {
    *piResult = iValue;
    return TRUE;
  } 

  else return FALSE;
}

Yukarıdaki yarış durumu, geçici anahtar sözcüğü veya InterlockedExchange işlevi kullanılarak onarılabilir ve fValueHasBeenComputed değeri TRUE olarak ayarlanmadan önce iValue değerinin tüm işlemciler için güncelleştirilmesini sağlayabilir.

Visual Studio 2005'den başlayarak, /volatile:ms modunda derlenmişse, derleyici geçici değişkenlerinde okuma işlemleri için alma semantiği kullanır ve geçici değişkenlerinde yazma işlemleri için yayın semantiği kullanır (CPU tarafından desteklendiğinde). Bu nedenle, örneği aşağıdaki gibi düzeltebilirsiniz:

volatile int iValue;
volatile BOOL fValueHasBeenComputed = FALSE;
extern int ComputeValue();

void CacheComputedValue()
{
  if (!fValueHasBeenComputed) 
  {
    iValue = ComputeValue();
    fValueHasBeenComputed = TRUE;
  }
}
 
BOOL FetchComputedValue(int *piResult)
{
  if (fValueHasBeenComputed) 
  {
    *piResult = iValue;
    return TRUE;
  } 

  else return FALSE;
}

Visual Studio 2003 ile geçicigeçici başvuruları sıralanır; derleyici geçici değişken erişimini yeniden sıralamaz. Ancak bu işlemler işlemci tarafından yeniden sıralanabilir. Bu nedenle, örneği aşağıdaki gibi düzeltebilirsiniz:

int iValue;
BOOL fValueHasBeenComputed = FALSE;
extern int ComputeValue();

void CacheComputedValue()
{
  if (InterlockedCompareExchange((LONG*)&fValueHasBeenComputed, 
          FALSE, FALSE)==FALSE) 
  {
    InterlockedExchange ((LONG*)&iValue, (LONG)ComputeValue());
    InterlockedExchange ((LONG*)&fValueHasBeenComputed, TRUE);
  }
}
 
BOOL FetchComputedValue(int *piResult)
{
  if (InterlockedCompareExchange((LONG*)&fValueHasBeenComputed, 
          TRUE, TRUE)==TRUE) 
  {
    InterlockedExchange((LONG*)piResult, (LONG)iValue);
    return TRUE;
  } 

  else return FALSE;
}

Kritik Bölüm Nesnelerini

Birbirine Bağlı Değişken Erişimi

Bekleme İşlevleri