Попытка продолжить с моей идеей, что с использованием как программных, так и аппаратных ограничений памяти я мог бы отключить оптимизацию вне порядка для конкретной функции внутри кода, который скомпилирован с оптимизацией компилятора , и поэтому я мог бы реализовать программное обеспечение семафор с использованием алгоритмов, такими как Peterson
или Deker
, что не требует внеочередного исполнения, я тестировал следующий код, который содержит как SW барьерный asm volatile("": : :"memory")
и ССАГПЗ предопределённого HW барьер __sync_synchronize
:Использование барьеров памяти для принудительного выполнения в порядке
#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;
}
Но выходной файл компиляции:
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
И если удалить барьеры и скомпилировать снова, я получаю:
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
и скомпилирован с gcc -Wall -O2
в Ubuntu 14.04.1 LTS, x86.
Ожидаемый результат заключался в том, что выходной файл кода, который содержит барьеры памяти, будет содержать все присвоения значений, которые у меня есть в моем исходном коде, с ними mfence
.
Согласно связанному StackOverflow пост -
gcc memory barrier __sync_synchronize vs asm volatile("": : :"memory")
Добавляя ассемблерные на каждой итерации, НКУ не разрешается изменять порядок операций мимо барьера
И далее:
Howe ver, когда ЦП выполняет этот код, разрешается переупорядочивать операции «под капотом», если он не разбивает память модели . Это означает, что выполнение операций может быть выполнено не в порядке (если процессор поддерживает это, как и большинство в наши дни). A HW ограждение предотвратило бы это.
Но как вы можете видеть, единственное различие между кодом с барьерами памяти и кода без них является то, что бывший один содержит так, как я не ожидал увидеть его mfence
, а не все задания включены.
Почему выходной файл файла с барьерами памяти был не таким, каким я ожидал? Почему порядок mfence
был изменен? Почему компилятор удалил некоторые из заданий? Предоставляет ли компилятор такую оптимизацию, даже если применяется барьер памяти и разделяет каждую строку кода?
Ссылки на типы барьерных памяти и использования:
барьеры памяти - http://bruceblinn.com/linuxinfo/MemoryBarriers.html
GCC встроенных команд - https://gcc.gnu.org/onlinedocs/gcc-4.4.3/gcc/Atomic-Builtins.html
Терминология: ** Нарушение исполнения отличается от памяти переупорядочивание **. Даже процессоры в заказе конвейерны и извлекают выгоду из буфера хранилища, особенно для магазинов, которые пропускают L1. (https://en.wikipedia.org/wiki/MESI_protocol#Memory_Barriers). Как только они, как известно, не спекулятивны, их можно отслеживать только логикой упорядочения памяти (для принудительного упорядочивания StoreStore и LoadStore, если это необходимо), пока они на самом деле зафиксировать кэш L1 после того, как трубопровод забыли о них.) 'MFENCE' не сериализует конвейер; он только сериализует порядок, когда операции памяти становятся глобально видимыми. –