说到C++的并发内存模型,Stroustrup的C++编程语言,第4版,第1节.41.2.1,说:
...(像大多数现代硬件一样)机器无法加载或存储任何小于单词的东西.
但是,我的x86处理器,几年前,可以存储小于一个字的对象.例如:
#include <iostream>
int main()
{
char a = 5;
char b = 25;
a = b;
std::cout << int(a) << "\n";
return 0;
}
Run Code Online (Sandbox Code Playgroud)
如果没有优化,GCC将其编译为:
[...]
movb $5, -1(%rbp) # a = 5, one byte
movb $25, -2(%rbp) # b = 25, one byte
movzbl -2(%rbp), %eax # load b, one byte, not extending the sign
movb %al, -1(%rbp) # a = b, one byte
[...]
Run Code Online (Sandbox Code Playgroud)
评论是由我提出的,但是汇编是由GCC提出的.当然,它运行良好.
显然,我不明白Stroustrup在谈到硬件可以加载和存储任何小于一个单词的内容时所说的内容.据我所知,我的计划什么也不做,但加载和存储对象小于一个字的.
C++对零成本,硬件友好的抽象的彻底关注使C++与其他易于掌握的编程语言区别开来.因此,如果Stroustrup在公交车上有一个有趣的信号心理模型,或者有其他类似的东西,那么我想了解Stroustrup的模型.
什么是 Stroustrup谈论,拜托?
更长时间的背景声明 …
一个普遍的说法是,缓存中的字节存储可能导致内部读 - 修改 - 写周期,或者与存储完整寄存器相比会损害吞吐量或延迟.
但我从未见过任何例子.没有x86 CPU是这样的,我认为所有高性能CPU也可以直接修改缓存行中的任何字节.一些微控制器或低端CPU是否有不同之处,如果它们有缓存的话?
(我不计算字可寻址的机器,或者字节可寻址但没有字节加载/存储指令的Alpha.我说的是ISA本身支持的最窄的存储指令.)
在我的研究中回答现代x86硬件可以不将单个字节存储到内存中吗?,我发现Alpha AXP省略字节存储的原因假设它们被实现为真正的字节存储到缓存中,而不是包含字的RMW更新.(因此,它会使L1d缓存的ECC保护更加昂贵,因为它需要字节粒度而不是32位).
所有现代架构(早期Alpha除外)都可以对不可缓存的内存(而不是RMW周期)进行真正的字节加载/存储,这对于为具有相邻字节I/O寄存器的设备编写设备驱动程序是必需的.(例如,使用外部启用/禁用信号来指定更宽总线的哪些部分保存实际数据,例如此ColdFire CPU /微控制器上的2位TSIZ(传输大小),或者像PCI/PCIe单字节传输,或者像DDR一样SDRAM控制信号掩盖选定的字节.)
对于微控制器设计,可能需要在缓存中为字节存储执行RMW循环,即使它不是针对像Alpha这样的SMP服务器/工作站的高端超标量流水线设计?
我认为这种说法可能来自可以用字寻址的机器.或者来自未对齐的32位存储,需要在许多CPU上进行多次访问,并且人们错误地将其从一般存储到字节存储.
为了清楚起见,我希望到同一地址的字节存储循环将在每次迭代中以与字存储循环相同的周期运行.因此,对于填充阵列,32位存储可以比8位存储快4倍.(也许如果少了32位门店饱和的内存带宽,但8位店家没有.)但是,除非字节存储有一个额外的惩罚,你不会得到更超过4倍的速度差.(或者无论宽度是多少).
而我在谈论asm.一个好的编译器会自动向量化C中的字节或int存储循环,并使用更宽的存储或目标ISA上的最佳存储.
; x86-64 NASM syntax
mov rdi, rsp
; RDI holds at a 32-bit aligned address
mov ecx, 1000000000
.loop: ; do {
mov byte [rdi], al
mov byte [rdi+2], dl ; store two bytes in the same dword
; no pointer increment, this is the same 32-bit dword every time
dec ecx
jnz …Run Code Online (Sandbox Code Playgroud) 该MASKMOVDQU 1是86存储指令之间特殊的,因为在原则上,它可以让你各个字节存储在缓存行,而无需首先加载整个高速缓存行一路核心,以便写入的字节可以与不合并- 覆盖现有字节。
它似乎使用与 NT 存储相同的机制来工作:在不首先执行 RFO 的情况下将缓存行向下推。根据英特尔软件开发手册(重点是我的):
MASKMOVQ 指令可用于提高需要逐字节合并数据的算法的性能。它不应导致读取所有权;这样做会产生不必要的带宽,因为将使用字节掩码直接写入数据,而无需在存储之前分配旧数据。
然而,与其他 NT 存储不同,您可以使用掩码来指定实际写入的字节。
如果您想在不太可能适合任何级别的缓存的大区域中进行稀疏字节粒度写入,则此指令似乎是个主意。
与几乎所有其他有用的指令不同,英特尔没有将指令扩展到 AVX/AVX2 或 AVX-512 中的 256 或 512 位。这是否表示不再推荐使用此指令,可能无法在当前或未来的架构上有效实现?
1 ...及其在 MMX MASKMOVQ 中的64 位前身。
x86 ×4
arm ×1
assembly ×1
c++ ×1
caching ×1
concurrency ×1
cpu-cache ×1
intel ×1
memory-model ×1
optimization ×1
performance ×1