2012-12-23 6 views
5

Nehmen wir an, ich eine Klasse, die wie diese (eigentlich genau dieser Größe) aussieht:Ist ein geteilter Mutex effizienter als ein Atom einer relativ großen Struktur?

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

Wie Sie sehen können, diese Thread-sicher sein sollte. Die Update-Funktion wird von jeweils einem Thread, unbekannt, aber nur einem Thread (garantiert) aufgerufen, aber der Accessor kann von mehreren Threads gleichzeitig aufgerufen werden.

Die Update-Funktion ändert alle Werte und wird sehr oft aufgerufen (ein Hundertstel pro Sekunde). Die aktuelle Implementierung wird, wie Sie sich vorstellen können, viel blockieren.

Ich überlegte, std :: atomic zu verwenden, um Sperren zu vermeiden und diesen Code möglicherweise effizienter zu machen. Ich brauche jedoch wirklich die Update-Funktion, um die Werte zusammen zu aktualisieren. Daher Ich erwäge ähnliche anstatt etwas zu tun:

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

Mein gegenwärtiges Verständnis von std :: Atom ist, dass, selbst wenn dieser Code besser lesbar als die vorherigen ist (weil es nicht Sperre Erklärungen hat überall), Da die K :: Data-Struktur "groß" ist, wird std :: atomic nur mit einer normalen Mutex-Sperre implementiert (sollte also sowieso nicht schneller sein als meine ursprüngliche Implementierung).

Bin ich richtig?

Antwort

8

Jede Spezialisierung für std: atomic für eine Struktur wie diese wird internes Sperren beinhalten, so dass Sie nichts gewonnen haben, und jetzt haben Sie auch ein Datenrennen zwischen der Ladung und dem Speicher, die Sie vorher nicht hatten Dieses hatte in der vorherigen Version eine exklusive Sperre um den gesamten Block (nehme ich an?).

Auch mit dem shared_mutex, ist es vielleicht ratsam, mit einem normalen Mutex vs shared_mutex zu profilieren, Sie können den normalen Mutex besser durchführen (alles hängt davon ab, wie lange Sie Ihre Sperren halten). Der Vorteil von shared_mutex wird nur gesehen, wenn Sperren für einen längeren Zeitraum gehalten werden und es sehr wenig Schreibvorgänge gibt, da sonst der Overhead, der in shared_mutex involviert ist, alle Gewinne zunichte macht, die Sie gegenüber dem normalen Mutex haben würden.

+0

Ich wusste das nicht für shared_mutex ... danke für die Info! – Klaim

1

std :: atomic ist nicht unbedingt langsamer als std :: mutex. Zum Beispiel in MSVC 14,0, Implementierung von std :: atomic.store sieht wie folgt aus:

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

Es ist nicht, dass Spin-Lock gewährleistet wäre schneller als eine richtige std :: Mutex. Es hängt davon ab, was genau du tust. Aber std :: atomic ist sicher NICHT IMMER eine suboptimale Lösung im Vergleich zu std :: mutex.

Verwandte Themen