- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
谈到C++的并发内存模型,Stroustrup的C++编程语言,第4版,Sect。 41.2.1说:
... (like most modern hardware) the machine could not load or store anything smaller than a word.
#include <iostream>
int main()
{
char a = 5;
char b = 25;
a = b;
std::cout << int(a) << "\n";
return 0;
}
[...]
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
[...]
Consider what might happen if a linker allocated [variables of
char
type like]c
andb
in the same word in memory and (like most modern hardware) the machine could not load or store anything smaller than a word.... Without a well-defined and reasonable memory model, thread 1 might read the word containingb
andc
, changec
, and write the word back into memory. At the same time, thread 2 could do the same withb
. Then, whichever thread managed to read the word first and whichever thread managed to write its result back into memory last would determine the result....
最佳答案
TL:DR:在每个具有字节存储指令(包括x86)的现代ISA上,它们都是原子的,不会干扰周围的字节。 (我不知道任何较旧的ISA都可以通过字节存储指令“发明写”到相邻的字节。)
实际的实现机制(in non-x86 CPUs)有时是内部RMW周期,用于修改高速缓存行中的整个单词,但这是在内核内部“无形地”完成的,尽管它拥有高速缓存行的专有所有权,所以这仅是性能问题,而不是正确性。 (并且合并到存储缓冲区中有时可以将字节存储指令转换为对L1d缓存的有效全字提交。)
关于Stroustrup的措辞
我认为这不是一个非常准确,清晰或有用的声明。准确地说,现代CPU无法加载或存储小于高速缓存行的任何内容。 (尽管对于不可缓存的内存区域(例如MMIO)并非如此。)
也许只是做一个假设的例子来谈论内存模型,而不是暗示真正的硬件就是这样,这会更好。但是,如果我们尝试一下,我们也许会发现并没有那么明显或完全错误的解释,这可能是Stroustrup在写这篇介绍内存模型主题时所考虑的。 (很抱歉,这个答案很长;我在猜测他可能意味着什么以及有关相关主题的过程中写了很多篇文章……)
也许这是另一种情况,即高级语言设计师不是硬件专家,或者至少偶尔会做出错误的陈述。
我认为Stroustrup在谈论CPU如何在内部工作以实现字节存储指令。他建议没有明确定义和合理的内存模型的CPU可能在缓存行或没有缓存的CPU的内存中使用包含字的非原子RMW来实现字节存储。
对于高性能x86 CPU ,即使对内部(外部不可见)行为的这种虚弱说法也不是正确的。现代的Intel CPU不会对字节存储,甚至是未对齐的字或 vector 存储(不跨越高速缓存行边界)的吞吐量造成损失。 AMD是相似的。
如果字节存储区或未对齐存储区必须像提交给L1D高速缓存的存储区那样执行RMW周期,则它将以我们可以使用性能计数器进行测量的方式来干扰存储区和/或加载指令/uop吞吐量。 (在经过精心设计的实验中,避免了在提交到L1d高速缓存之前隐藏存储缓冲区中存储合并的可能性,因为存储执行单元在当前CPU上每个时钟只能运行1个存储。)
但是,某些非x86 ISA的高性能设计确实使用原子RMW周期在内部将存储提交到L1d缓存。 Are there any modern CPUs where a cached byte store is actually slower than a word store? 高速缓存行始终始终处于MESI Exclusive/Modified状态,因此它不会带来任何正确性问题,仅会对性能造成很小的影响。这与可能在其他CPU上增加存储空间的操作有很大的不同。 (以下关于未发生这种情况的论点仍然适用,但是我的更新可能遗漏了一些仍然认为原子高速缓存-RMW不太可能的东西。)
(在许多非x86 ISA上,根本不支持未对齐的存储,或者使用未对齐的存储比在x86软件中使用的存储少。弱排序的ISA允许在存储缓冲区中进行更多的合并,因此实际上,由于字节存储指令的数量较少,因此单行存储没有这些花哨的(耗电的)高速缓存访问硬件动机,在某些设计中,用于分散字节存储的RMW字是可以接受的折衷方案。)
Alpha AXP 是1992年的高性能RISC设计,著名的(在现代非DSP ISA中是唯一的)省略了字节加载/存储指令,直到 Alpha 21164A (EV56) in 1996为止。显然,他们不认为word-RMW是实现字节存储的可行选择,因为仅实现32位和64位对齐存储所提到的优势之一是L1D缓存的更高效ECC。 "Traditional SECDED ECC would require 7 extra bits over 32-bit granules (22% overhead) versus 4 extra bits over 8-bit granules (50% overhead)."(@Paul A. Clayton关于字与字节寻址的答案还有其他一些有趣的计算机体系结构方面的内容。)如果字节存储是通过word-RMW实现的,您仍然可以使用字粒度进行错误检测/纠正。
因此,当前的Intel CPU仅在L1D中使用奇偶校验(而不是ECC)。请参阅this Q&A,以了解有关硬件(不是消除“静默存储”)的信息:在写操作之前检查高速缓存的旧内容,以避免如果匹配的行将其标记为脏行将需要RMW而不是仅存储,这是一个主要障碍。
事实证明,一些高性能的流水线设计确实使用了原子字RMW来提交给L1d,尽管它使内存流水线停滞了,但是(正如我在下文所述),任何对RAM进行外部可见的RMW的可能性都较小。
Word-RMW也不是MMIO字节存储的有用选项,因此,除非您的体系结构不需要IO的子字存储,否则您需要对IO进行某种特殊处理(例如Alpha's sparse I/O space,其中要加载字)/stores被映射到字节加载/stores,因此它可以使用商用PCI卡,而不需要没有字节IO寄存器的特殊硬件。
像@Margaret points out一样,DDR3存储器 Controller 可以通过设置掩盖突发的其他字节的控制信号来进行字节存储。将这些信息获取到内存 Controller (用于未缓存的存储)的相同机制也可以使该信息与装入或存储一起传递到MMIO空间。因此,有一些硬件机制可以真正做到
一个字节存储,即使是在面向突发的存储系统上,现代CPU也很可能会使用它而不是实现RMW,因为它可能更简单,并且对MMIO的正确性要好得多。
How many and what size cycles will be needed to perform longword transferred to the CPU展示了ColdFire微 Controller 如何通过外部信号线发送传输大小(字节/字/长字/16字节线)的信号,允许它进行字节加载/存储,即使32位宽的内存已连接到其32位位数据总线。对于大多数内存总线设置来说,大概是这样的事情(但我不知道)。 ColdFire示例也很复杂,因为它还可以配置为使用16位或8位内存,从而花费更多的时间进行更广泛的传输。但请记住,重要的一点是它具有用于传输大小的外部信号,以告知存储器HW实际正在写入哪个字节。
Stroustrup的next paragraph是
"The C++ memory model guarantees that two threads of execution can update and access separate memory locations without interfering with each other. This is exactly what we would naively expect. It is the compiler’s job to protect us from the sometimes very strange and subtle behaviors of modern hardware. How a compiler and hardware combination achieves that is up to the compiler. ..."
char
,就像Stroustrup所谈论的那种编译器解决方法一样。)xchg
或add
,它也需要ALU和寄存器访问权限,因为涉及的所有硬件都在同一个流水线阶段,这可能会停顿一个或两个额外的周期。这显然对性能不利,并且需要额外的硬件以允许该管道阶段表明其正在停止。这不一定与Stroustrup的第一个主张相抵触,因为他在谈论的是没有内存模型的假设ISA,但这仍然很麻烦。char
。否则,如果无法证明没有其他线程可以通过指针写入相邻字节,则它必须与LL/SC一起使用atomic-RMW软件。movzx/movsx
避免部分注册错误的依赖关系或合并停顿,在x86上字节加载就很便宜。 x86的主要缺点是,您需要单独的加载指令,而不是将内存操作数用作ALU指令的源(如果您要向32位整数添加零扩展字节),则可以节省前端uop吞吐量带宽和代码大小。或者,如果您只是将一个字节添加到字节寄存器,则x86基本上没有任何缺点。 RISC负载存储ISA始终始终需要单独的负载和存储指令。 x86字节存储不再比32位存储昂贵。movsx
放入其自己的字中,并在可能的情况下使用字加载/存储(例如,针对结构外部的全局变量以及堆栈中的局部变量)。 IDK(如果有MIPS/ARM/任何真正的实现)或慢速字节加载/存储,但如果是,则gcc可以使用movzx
选项来控制它。char
/ -mtune=
needs an extra ALU uop, but otherwise zero/sign extension is handled right in the load port on Intel and AMD CPUs.,或者在您不知道char[]
指向何处时取消引用。 (这包括您要用于MMIO的char *
。)因此,让编译器和链接器将volatile char*
变量放在单独的单词中并不是一个完整的解决方案,如果真正的字节存储速度很慢,则仅仅是性能上的麻烦。From the Linux Alpha HOWTO.
When the Alpha architecture was introduced, it was unique amongst RISC architectures for eschewing 8-bit and 16-bit loads and stores. It supported 32-bit and 64-bit loads and stores (longword and quadword, in Digital's nomenclature). The co-architects (Dick Sites, Rich Witek) justified this decision by citing the advantages:
- Byte support in the cache and memory sub-system tends to slow down accesses for 32-bit and 64-bit quantities.
- Byte support makes it hard to build high-speed error-correction circuitry into the cache/memory sub-system.
Alpha compensates by providing powerful instructions for manipulating bytes and byte groups within 64-bit registers. Standard benchmarks for string operations (e.g., some of the Byte benchmarks) show that Alpha performs very well on byte manipulation.
关于c++ - 现代x86硬件能否不将单个字节存储到内存中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46721075/
美好的一天!我试图添加两个字节变量并注意到奇怪的结果。 byte valueA = 255; byte valueB = 1; byte valueC = (byte)(valueA + valueB
嗨,我是 swift 的新手,我正在尝试解码以 [Byte] 形式发回给我的字节数组?当我尝试使用 if let string = String(bytes: d, encoding: .utf8)
我正在使用 ipv4 和 ipv6 存储在 postgres 数据库中。 因为 ipv4 需要 32 位(4 字节)而 ipv6 需要 128(16 字节)位。那么为什么在 postgres 中 CI
我很好奇为什么 Go 不提供 []byte(*string) 方法。从性能的角度来看,[]byte(string) 不会复制输入参数并增加更多成本(尽管这看起来很奇怪,因为字符串是不可变的,为什么要复
我正在尝试为UDP实现Stop-and-Wait ARQ。根据停止等待约定,我在 0 和 1 之间切换 ACK。 正确的 ACK 定义为正确的序列号(0 或 1)AND消息长度。 以下片段是我的代码的
我在下面写了一些代码,目前我正在测试,所以代码中没有数据库查询。 下面的代码显示 if(filesize($filename) != 0) 总是转到 else,即使文件不是 0 字节而是 16 字节那
我使用 Apache poi 3.8 来读取 xls 文件,但出现异常: java.io.IOException: Unable to read entire header; 0 by
字典大小为 72 字节(根据 getsizeof(dict) 在字典上调用 .clear() 之后发生了什么,当新实例化的字典返回 240 字节时? 我知道一个简单的 dict 的起始大小为“8”,并
我目前正在努力创建一个函数,它接受两个 4 字节无符号整数,并返回一个 8 字节无符号长整数。我试图将我的工作基于 this research 描述的方法,但我的所有尝试都没有成功。我正在处理的具体输
看看这个简单的程序: #include using namespace std; int main() { unsigned int i=0x3f800000; float* p=(float*)(
我创建了自己的函数,将一个字符串转换为其等效的 BCD 格式的 bytes[]。然后我将此字节发送到 DataOutputStram (使用需要 byte[] 数组的写入方法)。问题出在数字字符串“8
此分配器将在具有静态内存的嵌入式系统中使用(即,没有可用的系统堆,因此“堆”将只是“char heap[4096]”) 周围似乎有很多“小型内存分配器”,但我正在寻找能够处理非常小的分配的一个。我说的
我将数据库脚本从 64 位系统传输到 32 位系统。当我执行脚本时,出现以下错误, Warning! The maximum key length is 900 bytes. The index 'U
想知道 128 字节 ext2 和 256 字节 ext3 文件系统之间的 inode 数据结构差异。 我一直在为 ext2、128 字节 inode 使用此引用:http://www.nongnu.
我试图理解使用 MD5 哈希作为 Cassandra key 在“内存/存储消耗”方面的含义: 我的内容(在 Java 中)的 MD5 哈希 = byte[] 长 16 个字节。 (16 字节来自维基
检查其他人是否也遇到类似问题。 shell脚本中的代码: ## Convert file into Unix format first. ## THIS is IMPORTANT. ###
我们有一个测量数据处理应用程序,目前所有数据都保存为 C++ float,这意味着在我们的 x86/Windows 平台上为 32 位/4 字节。 (32 位 Windows 应用程序)。 由于精度成
我读到在 Java 中 long 类型可以提升为 float 和 double ( http://www.javatpoint.com/method-overloading-in-java )。我想问
我有一个包含 n 个十进制元素的列表,其中每个元素都是两个字节长。 可以说: x = [9000 , 5000 , 2000 , 400] 这个想法是将每个元素拆分为 MSB 和 LSB 并将其存储在
我使用以下代码进行 AES-128 加密来编码一个 16 字节的 block ,但编码值的长度给出了 2 个 32 字节的 block 。我错过了什么吗? plainEnc = AES.enc
我是一名优秀的程序员,十分优秀!