使用内存屏障强制按顺序执行

时间:2016-08-03 10:51:43

标签: c gcc assembly compilation memory-barriers

尝试继续我的想法,使用软件和硬件内存障碍我可以禁用编译器优化编译的代码中的特定函数的无序优化,因此我可以使用软件信号量实现像PetersonDeker这样的算法不需要无序执行,我测试了以下包含SW barrier asm volatile("": : :"memory")和gcc builtin HW barrier __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

在Ubuntu 14.04.1 LTS,x86中使用gcc -Wall -O2编译。

预期的结果是包含内存屏障的代码的输出文件将包含我在源代码中的值的所有赋值,它们之间有mfence

根据相关的StackOverflow帖子 -

gcc memory barrier __sync_synchronize vs asm volatile("": : :"memory")

  

在每次迭代时添加内联汇编时,不允许gcc更改通过障碍的操作顺序

后来:

  

但是,当CPU执行此代码时,允许重新排序   操作“引擎盖下”,只要它不破坏记忆   订购模式。这意味着可以执行操作   乱序(如果CPU支持,就像大多数人这样做)。一个HW   栅栏会阻止它。

但正如您所看到的,代码与内存屏障和没有内存屏障的代码之间的唯一区别在于前者包含mfence的方式我不希望看到它,而不是所有的包括作业。

为什么带有内存屏障的文件的输出文件不符合我的预期 - 为什么mfence订单已被更改?为什么编译器会删除一些赋值?即使应用了内存屏障,编译器是否允许进行此类优化并分离每一行代码?

引用内存屏障类型和用法:

1 个答案:

答案 0 :(得分:4)

内存屏障告诉编译器/ CPU不应该跨屏障重新排序指令,它们并不意味着无论如何都必须完成无证据的写入。

如果您将x定义为volatile,则编译器无法做出假设,它是唯一关心x值并且必须遵循以下规则的实体C抽象机器,用于实际发生的内存写入。

在您的特定情况下,您可以跳过障碍,因为它已经保证不会相互重新排序易失性访问。

如果您有C11支持,最好使用_Atomic,这样可以保证不会对您的x重新排序正常分配,并且访问是原子的。

编辑:GCC(以及clang)似乎在这方面不一致,并不总是做这种优化。 I opened a GCC bug report regarding this.