2014-11-02 6 views
10

Damit eine typische moderne Prozessorarchitektur (wie x86_64) eine atomare Last oder einen Speicher ausführen kann, müssen natürlich die zu lesenden/schreibenden Daten ausgerichtet werden.Ausrichtung atomarer Variablen

Aber wie wird diese Anforderung tatsächlich realisiert/erzwungen über C++ 11 <atomic> Variablen?

Angenommen, ich habe eine Architektur, die 16-Byte-Vergleichs- und Auslagerungs (Doppelwort CAS) unterstützt, so kann es atomar Lesen/Schreiben von 16-Byte-Werte, und ich definieren einen 16-Byte-Typ:

struct double_word 
{ 
    std::uint64_t x; 
    std::uint64_t y; 
}; 
Jetzt

, nehme ich gehören std::atomic<double_word> als Mitglied Feld einer Klasse:

class foo 
{ 
    public: 

    std::atomic<double_word> dword; 
}; 

wie kann ich wissen foo::dword tatsächlich an einer Grenze 16 Byte ausgerichtet ist? Wie kann ich einen Anruf an dword.load() würde eigentlich Atom sein?

Eigentlich begann ich ursprünglich diese Frage wegen einer seltsamen Sache zu stellen, die passierte, als ich ein weiteres Datenelement vor foo::dword hinzufügte. I definiert foo als:

class foo 
{ 
    public: 

    std::uint64_t x; 
    std::atomic<double_word> dword; 
}; 

Wenn ich eine Atom Belastung foo::dword tatsächlich durchführen, und kompilieren und ausführen mit GCC 4.7.2 auf einem x86_64 Maschine läuft Debian Linux, es gibt mir tatsächlich einen Segmentation Fault!

Volles Programm:

#include <atomic> 
#include <cstdint> 

    struct double_word 
    { 
     std::uint64_t x; 
     std::uint64_t y; 
    }; 

    class foo 
    { 
     public: 

     std::uint64_t x; 
     std::atomic<double_word> dword; // <-- not aligned on 16-byte boundary 
    }; 

    int main() 
    { 
     foo f; 
     double_word d = f.dword.load(); // <-- segfaults with GCC 4.7.2 !! 
    } 

Das eigentlich Segfaults auf f.dword.load(). Zuerst habe ich nicht verstanden, warum, aber dann erkannte ich, dass dwordnicht auf einer 16-Byte-Grenze ausgerichtet ist. Das führt zu vielen Fragen wie: Was sollte der Compiler tun, wenn eine atomare Variable nicht ausgerichtet ist und wir versuchen, sie atomar zu laden? Ist es undefiniertes Verhalten? Warum segregierte das Programm einfach?

Zweitens, was sagt der C++ 11-Standard dazu? Sollte der Compiler sicherstellen, dass double_word automatisch auf eine 16-Byte-Grenze ausgerichtet wird? Wenn ja, bedeutet das, dass GCC hier einfach fehlerhaft ist? Wenn nicht - es scheint, es liegt an dem Benutzer, die Ausrichtung zu gewährleisten, in diesem Fall alle Zeit verwenden wir eine std::atomic<T> größer als ein Byte, würde es scheinen wir haben zu verwenden std::aligned_storage, um sicherzustellen, dass es richtig ausgerichtet ist , die (A) schwerfällig erscheint, und (B) etwas ist, was ich nie in der Praxis oder in irgendwelchen Beispielen/Tutorials gesehen habe.

Also, wie sollte ein Programmierer mit C++ 11 <atomic> Ausrichtung Probleme wie diese behandeln?

+3

Es sieht aus wie Fehler der Zusammenarbeit zwischen Compiler und Bibliothek.Der Code, der von C++ - Standard-segfaults funktionieren sollte. –

+3

GCC 4.7.2 ist alt und vor der Fertigstellung des C++ 11-Standards. Bitte wechseln Sie zu einer neueren Version von [GCC] (http://gcc.gnu.org/) (im November 2014, [GCC 4.9.2] (https://gcc.gnu.org/gcc-4.9/)) Das wäre viel mehr C++ 11 konform. –

+0

Wie jeder andere Programmierer, sehen Sie sich das Handbuch des Compilers an und sehen Sie, was der Compiler annimmt und wie Sie ihm sagen sollten, was ** Sie ** wollen. Ein weiterer Grund, Compiler-Handbücher zu lesen, da sie Changelogs und Hinweise darauf enthalten, welche Features nicht vollständig sind ... wie Atomics in 4.7.2. – BlamKiwi

Antwort

-1

Ich denke, es liegt in der Verantwortung des Programmierers sicherzustellen, dass dword 16bytes Ausrichtung ist. Auf 64-Bit-Plattformen werden Daten in 64-Bit-Grenzen ausgerichtet, sofern Sie nicht explizit angeben.

1

Es ist ein GCC-Bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65147 Hinzufügen alignas(16) ist das Problem behoben.

#include <atomic> 
#include <cstdint> 

struct double_word 
{ 
    std::uint64_t x; 
    std::uint64_t y; 
}; 

class foo 
{ 
    public: 

    std::uint64_t x; 
    alignas(16) std::atomic<double_word> dword; // <-- not aligned on 16-byte boundary 
}; 

int main() 
{ 
    foo f; 
    double_word d = f.dword.load(); // <-- segfaults with GCC 4.7.2 !! 
}