原子加载和存储函数生成与非原子加载和存储相同的汇编代码
Atomic load and store functions produce same assembly code as non-atomic load and store
为什么store_idx_x86() 的汇编输出与store_idx() 相同,load_idx_x86() 与load_idx() 相同?
据我了解,__atomic_load_n() 会刷新核心的失效队列,__atomic_store_n() 会刷新核心的存储缓冲区。
注意——我遵守了:gcc (GCC) 4.8.2 20140120 (Red Hat 4.8.2-16)
更新:我知道 x86 永远不会重新排序存储与其他存储和负载与其他负载——因此 gcc 是否足够聪明以仅在需要时或应该使用 __atomic_ 时实施 sfence 和 lfence 导致栅栏(假设内存模型比 __ATOMIC_RELAXED 更严格)?
代码
#include <stdint.h>
inline void store_idx_x86(uint64_t* dest, uint64_t idx)
{
*dest = idx;
}
inline void store_idx(uint64_t* dest, uint64_t idx)
{
__atomic_store_n(dest, idx, __ATOMIC_RELEASE);
}
inline uint64_t load_idx_x86(uint64_t* source)
{
return *source;
}
inline uint64_t load_idx(uint64_t* source)
{
return __atomic_load_n(source, __ATOMIC_ACQUIRE);
}
程序集:
.file "util.c"
.text
.globl store_idx_x86
.type store_idx_x86, @function
store_idx_x86:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movq %rdi, -8(%rbp)
movq %rsi, -16(%rbp)
movq -8(%rbp), %rax
movq -16(%rbp), %rdx
movq %rdx, (%rax)
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size store_idx_x86, .-store_idx_x86
.globl store_idx
.type store_idx, @function
store_idx:
.LFB1:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movq %rdi, -8(%rbp)
movq %rsi, -16(%rbp)
movq -8(%rbp), %rax
movq -16(%rbp), %rdx
movq %rdx, (%rax)
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1:
.size store_idx, .-store_idx
.globl load_idx_x86
.type load_idx_x86, @function
load_idx_x86:
.LFB2:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movq %rdi, -8(%rbp)
movq -8(%rbp), %rax
movq (%rax), %rax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2:
.size load_idx_x86, .-load_idx_x86
.globl load_idx
.type load_idx, @function
load_idx:
.LFB3:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movq %rdi, -8(%rbp)
movq -8(%rbp), %rax
movq (%rax), %rax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE3:
.size load_idx, .-load_idx
.ident "GCC: (GNU) 4.8.2 20140120 (Red Hat 4.8.2-16)"
.section .note.GNU-stack,"",@progbits
Why is the assembly output of store_idx_x86() the same as store_idx() and load_idx_x86() the same as load_idx()?
在 x86 上,假定编译器强制对齐,它们是相同的操作。加载和存储到本机大小或更小的对齐地址保证是原子的。参考Intel manual vol 3A, 8.1.1:
The Pentium processor (and newer processors since) guarantees that the following additional memory operations
will always be carried out atomically: Reading or writing a quadword aligned on a 64-bit boundary [...]
此外,x86 强制执行强有序内存模型,这意味着每个存储和加载分别具有隐式释放和获取语义。
最后,您提到的防护指令仅在使用英特尔的非时间 SSE 指令(great reference here), or when needing to create a store-load fence (article here)时才需要(实际上是 mfence
或 lock
指令) .
旁白:我对英特尔手册中的那句话很好奇,所以我 devised a test program。令人沮丧的是,在我的计算机(2 核 i3-4030U)上,我从中得到了这个输出:
unaligned
4265292 / 303932066 | 1.40337%
unaligned, but in same cache line
2373 / 246957659 | 0.000960893%
aligned (8 byte)
0 / 247097496 | 0%
这似乎违反了英特尔的说法。我会调查的。同时,您应该克隆该演示程序并查看它能为您提供什么。您只需要在 linux.
上 -std=c++11 ... -pthread
为什么store_idx_x86() 的汇编输出与store_idx() 相同,load_idx_x86() 与load_idx() 相同?
据我了解,__atomic_load_n() 会刷新核心的失效队列,__atomic_store_n() 会刷新核心的存储缓冲区。
注意——我遵守了:gcc (GCC) 4.8.2 20140120 (Red Hat 4.8.2-16)
更新:我知道 x86 永远不会重新排序存储与其他存储和负载与其他负载——因此 gcc 是否足够聪明以仅在需要时或应该使用 __atomic_ 时实施 sfence 和 lfence 导致栅栏(假设内存模型比 __ATOMIC_RELAXED 更严格)?
代码
#include <stdint.h>
inline void store_idx_x86(uint64_t* dest, uint64_t idx)
{
*dest = idx;
}
inline void store_idx(uint64_t* dest, uint64_t idx)
{
__atomic_store_n(dest, idx, __ATOMIC_RELEASE);
}
inline uint64_t load_idx_x86(uint64_t* source)
{
return *source;
}
inline uint64_t load_idx(uint64_t* source)
{
return __atomic_load_n(source, __ATOMIC_ACQUIRE);
}
程序集:
.file "util.c"
.text
.globl store_idx_x86
.type store_idx_x86, @function
store_idx_x86:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movq %rdi, -8(%rbp)
movq %rsi, -16(%rbp)
movq -8(%rbp), %rax
movq -16(%rbp), %rdx
movq %rdx, (%rax)
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size store_idx_x86, .-store_idx_x86
.globl store_idx
.type store_idx, @function
store_idx:
.LFB1:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movq %rdi, -8(%rbp)
movq %rsi, -16(%rbp)
movq -8(%rbp), %rax
movq -16(%rbp), %rdx
movq %rdx, (%rax)
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1:
.size store_idx, .-store_idx
.globl load_idx_x86
.type load_idx_x86, @function
load_idx_x86:
.LFB2:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movq %rdi, -8(%rbp)
movq -8(%rbp), %rax
movq (%rax), %rax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2:
.size load_idx_x86, .-load_idx_x86
.globl load_idx
.type load_idx, @function
load_idx:
.LFB3:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movq %rdi, -8(%rbp)
movq -8(%rbp), %rax
movq (%rax), %rax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE3:
.size load_idx, .-load_idx
.ident "GCC: (GNU) 4.8.2 20140120 (Red Hat 4.8.2-16)"
.section .note.GNU-stack,"",@progbits
Why is the assembly output of store_idx_x86() the same as store_idx() and load_idx_x86() the same as load_idx()?
在 x86 上,假定编译器强制对齐,它们是相同的操作。加载和存储到本机大小或更小的对齐地址保证是原子的。参考Intel manual vol 3A, 8.1.1:
The Pentium processor (and newer processors since) guarantees that the following additional memory operations will always be carried out atomically: Reading or writing a quadword aligned on a 64-bit boundary [...]
此外,x86 强制执行强有序内存模型,这意味着每个存储和加载分别具有隐式释放和获取语义。
最后,您提到的防护指令仅在使用英特尔的非时间 SSE 指令(great reference here), or when needing to create a store-load fence (article here)时才需要(实际上是 mfence
或 lock
指令) .
旁白:我对英特尔手册中的那句话很好奇,所以我 devised a test program。令人沮丧的是,在我的计算机(2 核 i3-4030U)上,我从中得到了这个输出:
unaligned
4265292 / 303932066 | 1.40337%
unaligned, but in same cache line
2373 / 246957659 | 0.000960893%
aligned (8 byte)
0 / 247097496 | 0%
这似乎违反了英特尔的说法。我会调查的。同时,您应该克隆该演示程序并查看它能为您提供什么。您只需要在 linux.
上-std=c++11 ... -pthread