← Return to Home

False Sharing

2026-01-31 · 3 min read

CPU'larda bellek , cache line adı verilen sabit boyutlu bloklar (çoğunlukla 64 bayt) halinde önbelleğe (cache) alınır. False sharing, birbirinden bağımsız değişkenlerin aynı cache line içerisinde yer alması ve bu değişkenlerin farklı çekirdekler tarafından eşzamanlı olarak değiştirilmesi durumunda ortaya çıkar.

Linux Kernel dokümantasyonunda verilen aşağıdaki yapıyı göz önüne alalım:

struct foo {
    refcount_t refcount;
    ...
    char name[16];
} ____cacheline_internodealigned_in_smp;

refcount (A) ve name (B) üyeleri (member) aynı cache line'ı paylaşır:

              +-----------+                     +-----------+
              |   CPU 0   |                     |   CPU 1   |
              +-----------+                     +-----------+
             /                                        |
            /                                         |
           V                                          V
       +----------------------+             +----------------------+
       | A      B             | Cache 0     | A       B            | Cache 1
       +----------------------+             +----------------------+
                           |                  |
---------------------------+------------------+-----------------------------
                           |                  |
                         +----------------------+
                         |                      |
                         +----------------------+
            Main Memory  | A       B            |
                         +----------------------+

name nesne yaratılırken atanır ve sonrasında sadece okunur ancak, refcount sık sık güncellenir. Aynı cache line'da oldukları için refcount güncellendiğinde cache invalidate edilir ve okuma yapan diğer CPU'ların cache line'ı yeniden yüklemesi gerekir.

Her cache line paylaşımı problem değildir, performans sorunu yaratması için genellikle şu koşullar gereklidir:

  1. Birden fazla çekirdek tarafından erişilen veri
  2. Bu veriye, en az biri yazma operasyonu (yazma/okuma veya yazma/yazma) olmak üzere eşzamanlı erişim

Linux üzerinde, perf c2c ve pahole gibi araçlar kullanılarak false sharing problemleri tespit edilebilir.

False sharingden kaçınmak için Linux Kernel dokümantasyonunda aşağıdaki yöntemler belirtilmiştir:

  1. Sık erişilen (hot) global veriyi ayrı bir cache line'a taşımak:
#include <stdalign.h>
#include <stdint.h>

#ifndef CACHELINE_SIZE
#define CACHELINE_SIZE 64
#endif

alignas(CACHELINE_SIZE) volatile uint64_t tcp_memory_allocated = 0;

Bu yöntem daha fazla bellek kullanımına neden olabilir.

  1. False sharing’e neden olan alanlar aynı veri yapısı içinde bulunuyorsa, bu alanlar farklı cache line’lara düşecek şekilde yeniden düzenlenebilir:
#include <stdint.h>
#include <stdalign.h>

#ifndef CACHELINE_SIZE
#define CACHELINE_SIZE 64
#endif

struct stats_bad {
    // HOT: sık yazılan
    volatile uint64_t hot_counter;

    // COLD: çoğunlukla okunan / nadir yazılan
    char name[32];
    uint32_t flags;
};

struct stats_good {
    // HOT: tek başına bir cache line'a yakın konumlandır
    alignas(CACHELINE_SIZE) volatile uint64_t hot_counter;

    // Diğer alanların aynı cache line'ı paylaşmasını önle
    char _pad[CACHELINE_SIZE - sizeof(uint64_t)];

    // COLD
    char name[32];
    uint32_t flags;
};

Yeni yerleşim başka alanlar arasında yeni false sharing problemleri yaratabilir, bu nedenle bu yöntem dikkatli kullanılmalıdır.

  1. False sharing’in temel tetikleyicisi write operasyonlarıdır. Bu nedenle mümkün olan her yerde koşulsuz write yerine, read-then-write (compare-then-write) yaklaşımı tercih edilmelidir:
set_bit(XXX); // bunun yerine

if (!test_bit(XXX))
    set_bit(XXX);
  1. Sık güncellenen global sayaçlar yerine, per-cpu data + global aggregation yöntemleri kullanılmalıdır. Thread-local değişkenler belirli periyotlarla aggregate edilip global değişkene atanabilir.

Bir sonraki yazıda görüşmek üzere.

Kaynaklar