Próbowałem kontynuować myśl, że używając zarówno zapór pamięci programowych, jak i sprzętowych, mogę wyłączyć optymalizację poza kolejnością dla określonej funkcji wewnątrz kodu skompilowanego z optymalizacją kompilatora i dlatego może implementować oprogramowanie semafora użyciu algorytmów jak Peterson
lub Deker
, że nie wymaga wykonania poza kolejnością, i testowano następujące kodu z zaporowej SW asm volatile("": : :"memory")
i gcc Builtin HW barierę __sync_synchronize
:Używanie barier pamięci do wymuszania wykonywania w kolejności
#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;
}
Ale plik wyjściowy kompilacji to:
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
A jeśli usunąć bariery i skompilować ponownie, otrzymuję:
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
zarówno skompilowany z gcc -Wall -O2
w Ubuntu 14.04.1 LTS, x86.
Spodziewanym rezultatem było to, że plik wyjściowy kodu zawierającego bariery pamięci będzie zawierał wszystkie przypisania wartości, które mam w moim kodzie źródłowym, z mfence
między nimi.
Według powiązanej StackOverflow postu -
gcc memory barrier __sync_synchronize vs asm volatile("": : :"memory")
Dodając swój zespół inline na każdej iteracji, gcc nie jest dopuszczalne, aby zmienić kolejność operacji przeszłość bariery
A później:
Howe ver, kiedy procesor wykonuje ten kod, można zmienić kolejność operacji "pod maską", o ile nie powoduje to uszkodzenia modelu zamawiania pamięci. Oznacza to, że wykonanie tych operacji może być niemożliwe (jeśli procesor obsługuje to, jak większość robi obecnie). Ogrodzenie HW uniemożliwiłoby to.
Ale jak widać, jedyną różnicą między kodem z barier pamięci i kodu bez nich jest to, że były jeden zawiera mfence
w taki sposób, że nie spodziewano się go zobaczyć, a nie wszystkie zadania są uwzględnione.
Dlaczego plik wyjściowy pliku z barierami pamięci nie jest zgodny z oczekiwaniem? Dlaczego zamówienie mfence
zostało zmienione? Dlaczego kompilator usunął niektóre z zadań? Czy kompilator może dokonywać takich optymalizacji, nawet jeśli zastosowana jest bariera pamięci i oddziela każdą linię kodu?
Odniesienia do rodzaju bariera pamięci i użytkowania:
bariera pamięci - http://bruceblinn.com/linuxinfo/MemoryBarriers.html
GCC builtins - https://gcc.gnu.org/onlinedocs/gcc-4.4.3/gcc/Atomic-Builtins.html
Terminologia: ** wykonanie poza zleceniem jest oddzielone od zmiany kolejności pamięci **. Nawet procesory w kolejności są potokowe i korzystają z bufora pamięci, szczególnie w przypadku sklepów, których brakuje w L1. (https://en.wikipedia.org/wiki/MESI_protocol#Memory_Barriers. Gdy już wiadomo, że nie są spekulatywne, mogą być śledzone tylko za pomocą logiki zamawiania pamięci (w celu wymuszenia porządkowania StoreStore i LoadStore w razie potrzeby), dopóki zatwierdzić pamięć podręczną L1, po tym jak potok o nich zapomniał.) 'MFENCE' nie tworzy serialu potoku; zmienia się tylko w kolejności, w której operacje pamięci stają się globalnie widoczne. –