2012-12-23 7 views
5

Supponiamo che ho una classe che assomiglia a questo (in realtà esattamente questa dimensione):È un mutex condiviso più efficiente di un atomico di una struttura relativamente grande?

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; 
}; 

Come si può vedere, questo dovrebbe essere thread-safe. La funzione di aggiornamento è chiamata da un thread alla volta, sconosciuto ma solo un thread (garantito), ma l'accessor può essere richiamato da più thread contemporaneamente.

La funzione di aggiornamento sta cambiando tutti i valori e viene chiamata molto spesso (un centinaio di volte al secondo). L'implementazione attuale, come puoi immaginare, si blocca molto.

Stavo considerando l'utilizzo di std :: atomic per evitare blocchi e potenzialmente rendere questo codice più efficiente. Tuttavia, ho davvero bisogno della funzione di aggiornamento per aggiornare insieme i valori. Di conseguenza, sto pensando di fare qualcosa di simile a questo, invece:

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; 
}; 

mia attuale comprensione della std :: atomica è che, anche se questo codice è più leggibile rispetto alla precedente (perché non hanno le dichiarazioni di blocco ovunque), dato che la struttura di K :: Data è "grande", std :: atomic verrà implementato con un normale blocco mutex (quindi non dovrebbe essere più veloce della mia implementazione iniziale comunque).

Sono corretto?

risposta

8

Qualsiasi specializzazione per std: atomic per una struttura come quella implicherà il blocco interno, quindi non hai guadagnato nulla, e ora hai anche una corsa di dati tra il carico e lo store che non avevi prima, come questo aveva un blocco esclusivo attorno all'intero blocco (presumo?) nella versione precedente.

Anche con il parametro shared_mutex, potrebbe essere consigliabile creare un profilo con un mutex normale con shared_mutex, è possibile che il mutex normale funzioni meglio (tutto dipende da quanto a lungo vengono trattenuti i blocchi).

Il beneficio della shared_mutex è visto solo quando le serrature sono detenuti per la lettura per un lungo periodo di tempo e ci sono pochissimi scrive, altrimenti l'overhead coinvolto nel shared_mutex uccide gli utili si avrebbe sopra il mutex normale.

+0

Non lo sapevo per shared_mutex ... grazie per le informazioni! – Klaim

1

std :: atomic non è necessariamente più lento di std :: mutex. Per esempio in MSVC 14,0, l'attuazione di std :: atomic.store assomiglia a questo:

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; 
} 

non è garantito che il blocco rotazione sarebbe stato più veloce di un adeguato std :: mutex. Dipende da cosa esattamente stai facendo. Ma std :: atomic è di sicuro NON SEMPRE una soluzione subottimale rispetto a std :: mutex.

Problemi correlati