- 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/
我需要将文本放在 中在一个 Div 中,在另一个 Div 中,在另一个 Div 中。所以这是它的样子: #document Change PIN
奇怪的事情发生了。 我有一个基本的 html 代码。 html,头部, body 。(因为我收到了一些反对票,这里是完整的代码) 这是我的CSS: html { backgroun
我正在尝试将 Assets 中的一组图像加载到 UICollectionview 中存在的 ImageView 中,但每当我运行应用程序时它都会显示错误。而且也没有显示图像。 我在ViewDidLoa
我需要根据带参数的 perl 脚本的输出更改一些环境变量。在 tcsh 中,我可以使用别名命令来评估 perl 脚本的输出。 tcsh: alias setsdk 'eval `/localhome/
我使用 Windows 身份验证创建了一个新的 Blazor(服务器端)应用程序,并使用 IIS Express 运行它。它将显示一条消息“Hello Domain\User!”来自右上方的以下 Ra
这是我的方法 void login(Event event);我想知道 Kotlin 中应该如何 最佳答案 在 Kotlin 中通配符运算符是 * 。它指示编译器它是未知的,但一旦知道,就不会有其他类
看下面的代码 for story in book if story.title.length < 140 - var story
我正在尝试用 C 语言学习字符串处理。我写了一个程序,它存储了一些音乐轨道,并帮助用户检查他/她想到的歌曲是否存在于存储的轨道中。这是通过要求用户输入一串字符来完成的。然后程序使用 strstr()
我正在学习 sscanf 并遇到如下格式字符串: sscanf("%[^:]:%[^*=]%*[*=]%n",a,b,&c); 我理解 %[^:] 部分意味着扫描直到遇到 ':' 并将其分配给 a。:
def char_check(x,y): if (str(x) in y or x.find(y) > -1) or (str(y) in x or y.find(x) > -1):
我有一种情况,我想将文本文件中的现有行包含到一个新 block 中。 line 1 line 2 line in block line 3 line 4 应该变成 line 1 line 2 line
我有一个新项目,我正在尝试设置 Django 调试工具栏。首先,我尝试了快速设置,它只涉及将 'debug_toolbar' 添加到我的已安装应用程序列表中。有了这个,当我转到我的根 URL 时,调试
在 Matlab 中,如果我有一个函数 f,例如签名是 f(a,b,c),我可以创建一个只有一个变量 b 的函数,它将使用固定的 a=a1 和 c=c1 调用 f: g = @(b) f(a1, b,
我不明白为什么 ForEach 中的元素之间有多余的垂直间距在 VStack 里面在 ScrollView 里面使用 GeometryReader 时渲染自定义水平分隔线。 Scrol
我想知道,是否有关于何时使用 session 和 cookie 的指南或最佳实践? 什么应该和什么不应该存储在其中?谢谢! 最佳答案 这些文档很好地了解了 session cookie 的安全问题以及
我在 scipy/numpy 中有一个 Nx3 矩阵,我想用它制作一个 3 维条形图,其中 X 轴和 Y 轴由矩阵的第一列和第二列的值、高度确定每个条形的 是矩阵中的第三列,条形的数量由 N 确定。
假设我用两种不同的方式初始化信号量 sem_init(&randomsem,0,1) sem_init(&randomsem,0,0) 现在, sem_wait(&randomsem) 在这两种情况下
我怀疑该值如何存储在“WORD”中,因为 PStr 包含实际输出。? 既然Pstr中存储的是小写到大写的字母,那么在printf中如何将其给出为“WORD”。有人可以吗?解释一下? #include
我有一个 3x3 数组: var my_array = [[0,1,2], [3,4,5], [6,7,8]]; 并想获得它的第一个 2
我意识到您可以使用如下方式轻松检查焦点: var hasFocus = true; $(window).blur(function(){ hasFocus = false; }); $(win
我是一名优秀的程序员,十分优秀!