2012-12-23 4 views
5

Załóżmy, że mam klasy, który wygląda tak (w rzeczywistości dokładnie tej wielkości):Czy wspólny muteks jest wydajniejszy od atomu o relatywnie dużej strukturze?

class K 
{ 
public: 

    long long get_x() const; // lock m_mutex in shared/read-only mode 

    void update(long long w); // lock m_mutex with a unique_lock 

private: 
    long long m_a; 
    long long m_b; 
    long long m_c; 
    long long m_x; 
    double m_flow_factor; 

    mutable boost::shared_mutex m_mutex; 
}; 

Jak widać, ten powinien być bezpieczny wątku. Funkcja aktualizacji jest wywoływana przez jeden wątek na raz, nieznany, ale tylko jeden wątek (gwarantowany), ale akcesor może być wywoływany przez kilka wątków w tym samym czasie.

Funkcja Aktualizacja zmienia wszystkie wartości i nazywa się bardzo często (hundread razy na sekundę). Obecna implementacja będzie, jak można się domyślić, bardzo blokuje.

Rozważałem użycie std :: atomic, aby uniknąć blokad i potencjalnie uczynić ten kod bardziej wydajnym. Jednak naprawdę potrzebuję funkcji aktualizacji, aby zaktualizować wartości razem. Dlatego jestem rozważa robi coś takiego zamiast:

class K 
{ 
public: 

    long long get_x() const 
    { return data.load().x; } 

    void update(long long w) 
    { 
     auto data_now = data.load(); 
     // ... work with data_now 
     data.store(data_now); 
    } 

private: 
    struct Data { 
    long long a; 
    long long b; 
    long long c; 
    long long x; 
    double flow_factor; 
    }; 
    std::atomic<Data> data; 
}; 

Moje obecne rozumienie std :: atomowa jest to, że nawet jeśli ten kod jest bardziej czytelny niż poprzedni (ponieważ nie mam deklaracje blokady wszędzie), jako struktura K :: Data jest "duża", std :: atomowa zostanie zaimplementowana z normalną blokadą mutex (więc i tak nie powinna być szybsza niż moja początkowa implementacja).

Czy mam rację?

Odpowiedz

8

Każda specjalizacja dla std: atomistyczna dla takiej struktury będzie wymagać wewnętrznego blokowania, więc nic nie zyskujesz, a teraz masz także wyścig danych między ładunkiem i sklepem, którego wcześniej nie miałeś, miało to wyłączne blokowanie wokół całego bloku (jak sądzę?) w poprzedniej wersji.

Również z shared_mutex, może być mądry profil z normalnym mutex vs shared_mutex, może się okazać, że normalny mutex działa lepiej (wszystko zależy od tego, jak długo trzymasz swoje zamki).

Korzyści z funkcji shared_mutex są widoczne tylko wtedy, gdy blokady są przechowywane do odczytu przez dłuższy czas i jest ich bardzo niewiele, w przeciwnym razie obciążenie związane z parametrem shared_mutex spowoduje utracenie wszystkich zysków, które można uzyskać w normalnym muteksie.

+0

Nie wiedziałem o tym dla shared_mutex ... dzięki za informację! – Klaim

1

std :: atomowy niekoniecznie jest wolniejszy niż std :: mutex. Na przykład w MSVC 14,0 realizacja std :: atomic.store wygląda następująco:

inline void _Atomic_copy(
volatile _Atomic_flag_t *_Flag, size_t _Size, 
    volatile void *_Tgt, volatile const void *_Src, 
     memory_order _Order) 
{ /* atomically copy *_Src to *_Tgt with memory ordering */ 
_Lock_spin_lock(_Flag); 
_CSTD memcpy((void *)_Tgt, (void *)_Src, _Size); 
_Unlock_spin_lock(_Flag); 
} 

inline void _Lock_spin_lock(
volatile _Atomic_flag_t *_Flag) 
{ /* spin until _Flag successfully set */ 
while (_ATOMIC_FLAG_TEST_AND_SET(_Flag, memory_order_acquire)) 
    _YIELD_PROCESSOR; 
} 

Nie jest zagwarantowane, że blokada wirowania byłoby szybciej niż właściwego std :: mutex. To zależy od tego, co dokładnie robisz. Ale std :: atomic na pewno NIE ZAWSZE jest nieoptymalnym rozwiązaniem w porównaniu do std :: mutex.

Powiązane problemy