Versuchen, mit meiner Idee, die Verwendung von Hardware- und Hardware-Speicherbarrieren fortzusetzen, die Out-of-Order-Optimierung für eine bestimmte Funktion in einem Code deaktivieren, der mit Compiler-Optimierung kompiliert wird und damit Software Semaphore unter Verwendung von Algorithmen wie Peterson
oder Deker
, die keine out-of-Order-Ausführung erfordert, habe ich den folgenden Code getestet, die sowohl Barriere asm volatile("": : :"memory")
und gcc SW enthält Barriere gebautet HW implementieren konnte ich __sync_synchronize
:Verwenden von Speicherbarrieren, um die Ausführung in der richtigen Reihenfolge zu erzwingen
#include <stdio.h>
int main(int argc, char ** argv)
{
int x=0;
asm volatile("": : :"memory");
__sync_synchronize();
x=1;
asm volatile("": : :"memory");
__sync_synchronize();
x=2;
asm volatile("": : :"memory");
__sync_synchronize();
x=3;
printf("%d",x);
return 0;
}
Aber die Kompilierungsausgabedatei ist:
main:
.LFB24:
.cfi_startproc
subq $8, %rsp
.cfi_def_cfa_offset 16
mfence
mfence
movl $3, %edx
movl $.LC0, %esi
movl $1, %edi
xorl %eax, %eax
mfence
call __printf_chk
xorl %eax, %eax
addq $8, %rsp
Und wenn ich die Barrieren entfernen und wieder kompilieren, erhalte ich:
main
.LFB24:
.cfi_startproc
subq $8, %rsp
.cfi_def_cfa_offset 16
movl $3, %edx
movl $.LC0, %esi
movl $1, %edi
xorl %eax, %eax
call __printf_chk
xorl %eax, %eax
addq $8, %rsp
beide kompiliert mit gcc -Wall -O2
in Ubuntu 14.04.1 LTS, x86.
Das erwartete Ergebnis war, dass die Ausgabedatei des Codes, der die Speicherbarrieren enthält, alle Zuordnungen der Werte enthält, die ich in meinem Quellcode habe, mit mfence
zwischen ihnen.
Nach einem verwandten Stackoverflow Post -
gcc memory barrier __sync_synchronize vs asm volatile("": : :"memory")
Wenn Ihre Inline-Assembler bei jeder Iteration das Hinzufügen, gcc ist nicht erlaubt, die Reihenfolge der Operationen über die Barriere zu ändern
Und später:
Howe Wenn die CPU diesen Code ausführt, ist es erlaubt, die Operationen "unter der Haube" neu zu ordnen, solange sie das Speichermodell nicht unterbrechen. Dies bedeutet, dass die Durchführung der Operationen außerhalb der Reihenfolge ausgeführt werden kann (wenn die CPU dies unterstützt, wie es die meisten heutzutage tun). Ein HW Zaun hätte das verhindert.
Aber wie Sie sehen können, ist der einzige Unterschied zwischen dem Code mit den Speicherbarrieren und den Code ohne sie ist, dass der frühere enthält mfence
in einer Weise, die ich nicht sehen erwartet wurde, und nicht alle Zuordnungen sind inklusive.
Warum ist die Ausgabedatei der Datei mit den Speicherbarrieren nicht wie erwartet - Warum wurde die mfence
Reihenfolge geändert? Warum hat der Compiler einige der Aufgaben entfernt? Darf der Compiler solche Optimierungen vornehmen, selbst wenn die Speicherbarriere angewendet wird und jede einzelne Codezeile trennt?
Verweise auf die Speicherbarrieretypen und Nutzung:
Speicher Barriers - http://bruceblinn.com/linuxinfo/MemoryBarriers.html
GCC Builtins - https://gcc.gnu.org/onlinedocs/gcc-4.4.3/gcc/Atomic-Builtins.html
Terminologie: ** Out-of-Order-Ausführung ist getrennt von Speicher-Neuordnung **. Sogar CPUs in der richtigen Reihenfolge sind pipelined und profitieren von einem Speicherpuffer, insbesondere für Filialen, die in L1 fehlen. (https://en.wikipedia.org/wiki/MESI_protocol#Memory_Barriers. Sobald bekannt ist, dass sie nicht spekulativ sind, können sie nur durch Speicherordnungslogik verfolgt werden (um die StoreStore- und LoadStore-Anordnung bei Bedarf durchzusetzen), bis sie tatsächlich funktionieren Commit zu L1-Cache, nachdem die Pipeline sie vergessen hat.) 'MFENCE' serialisiert die Pipeline nicht; Es serialisiert nur die Reihenfolge, in der Speichervorgänge global sichtbar werden. –