Assembly CPU如何读取双精度值?

Assembly CPU如何读取双精度值?,assembly,x86,cpu-architecture,Assembly,X86,Cpu Architecture,上面说: 注意,一个双变量将在32的8字节边界上分配 位机器,需要两个内存读取周期 这意味着x86CPU有一些指令可以读取一个double值(我认为x86CPU可以读取的最大值是4字节!)。有人能提供一个读取双精度值的指令的例子吗 有人能提供一个读取双精度值的指令的例子吗 加载8个字节并将xmm0的上半部分置零。 对于传统的x87,有两个。当然,您可以将内存操作数用于ALU指令,如addsd xmm0,[rsi] 或者使用AVX,有一些东西像vbroadcastsd-ymm0,[rsi],或者v

上面说:

注意,一个双变量将在32的8字节边界上分配 位机器,需要两个内存读取周期

这意味着x86CPU有一些指令可以读取一个
double
值(我认为x86CPU可以读取的最大值是4字节!)。有人能提供一个读取双精度值的指令的例子吗

有人能提供一个读取双精度值的指令的例子吗

加载8个字节并将xmm0的上半部分置零。
对于传统的x87,有两个。当然,您可以将内存操作数用于ALU指令,如
addsd xmm0,[rsi]

或者使用AVX,有一些东西像
vbroadcastsd-ymm0,[rsi]
,或者
vaddsd-xmm1,xmm0,[rsi]

所有这些都在所有现代x86 CPU上对单个uop进行解码,并对缓存进行单个访问

我认为x86 CPU可以读取的最大值是4字节

嗯?自8086年以来,8字节x87加载一直受支持。在64位模式下,
mov-rax、
pop-rax
都是8字节加载

使用AVX,您可以执行
vmovups ymm0、[rsi+rdx]
(即使在32位模式下)来执行32字节的加载。或者使用AVX512,
vmovups zmm0
进行64字节的加载或存储

在现代CPU上,缓存线是64字节,所以内存(逻辑上)在CPU内部和内核之间以64字节的块进行复制。Intel CPU在内核之间(以及在通往内存的途中在L2和L3之间)使用32字节总线


有关手册和(准确的)文档/指南/文章的大量良好链接,请参阅。如果像TutorialPoint或Geeksforgeks这样的网站上的内容看起来令人困惑,或者与您在其他地方阅读的内容不匹配,那么很有可能是错误的。没有这样的投票机制,不准确的内容就不会被剔除

让我们直接记录下来吧。它充满了错误的信息,它所说的关于硬件的大部分内容在1995年可能大致正确,但现在不是。即使在那个时候,很多逻辑/推理都是错误的

它甚至不包含“缓存”这个词!对于现代x86 CPU来说,谈论访问RAM和“银行”完全是胡说八道。同样的概念在某种程度上也适用于银行缓存,因此16B或32B等对齐边界对于某些CPU很重要,即使所有x86 CPU上的缓存线都是64B(在奔腾III之后)

现代x86通常具有非常好的未对齐访问支持,特别是对于8B及更窄的系统,但如果超过16B或在包括Ryzen在内的AMD CPU上,则可能会受到惩罚

在最新的处理器上,我们得到的
struct_c
大小为16字节。[……]

在使用相同工具集(GCC4.7)的旧处理器(AMD Athlon X2)上,我得到的
struct_c
大小为24字节。大小取决于内存库在硬件级别的组织方式

这显然是胡说八道<对于所有针对同一ABI的编译器,无论使用了什么
-march=pentium3
-mtune=znver1
设置,或者编译的硬件是什么,布局都必须相同,这样才能链接到传递的库(指向的指针)
struct
将代码中的类型键入库函数,反之亦然。一个明显的例子是,传递指针,内核在结构中写入字段。如果您的代码与内核在内存中的哪个字节表示哪个C
struct
成员方面不一致,那么您的代码将无法工作。指定布局/对齐规则(以及调用约定)是ABI的主要部分

很可能“较新”的测试针对的是32位i386 System V psABI,而“较旧”的测试针对的是x86-64 System V psABI(或Windows 32或64位,两者都有24字节
structc
,使用MSVC CL19)

因此,32位ABI将在结构内部对齐
double
,即使它更喜欢在其他位置将
double
对齐到8字节,但64位ABI不会对齐。i386 System V ABI可以追溯到很长一段时间,可能是实际的386或486 CPU,它们可能确实需要两个内存读取周期才能加载一个
double
。对于旧CPU或32位模式下的整数,仅考虑对齐边界(最多4B)的打包规则是有意义的。新设计的32位ABI可能需要对齐
double
,也可能需要对齐
int64\u t
(用于MMX/SSE2)。但是,破坏ABI兼容性,在
struct
s中对齐64位类型是不值得的

有关ABI文档,请参见标记wiki

请注意,
std::atomic
即使在
-m32
中也能获得完全的8B对齐

请注意,在32位机器上,将在8字节边界上分配一个双变量,并且需要两个内存读取周期

对64位对齐地址的qword加载或存储(例如
fld
fstp
)保证是原子的(自P5 Pentium以来),因此它肯定是对L1D缓存的单次访问(或对RAM的非缓存访问)。看

此保证适用于x86(包括AMD和其他供应商)。 事实上,
gcc-m32
使用SSE2
movq
或x87
fild
加载/存储来实现
std::atomic

较宽和/或未对齐的加载/存储不保证是单个访问,但在某些CPU上。e、 g.对于未跨越64B缓存线边界的未对齐数据,Intel Haswell/Skylake可以在每个周期执行两个32B未对齐向量加载,每个加载作为从L1D缓存的单个读取。如果它确实跨越缓存线边界(如
vmovups ymm0,[rdi+33]
其中
rdi
是64B对齐的),吞吐量将限制为每个周期一个,因为每个负载必须从两个缓存线读取和合并数据

对未对齐负载的硬件支持非常好,因此它只是协同工作
typedef struct structc_tag {
   char        c;
   double      d;
   int         s;
} structc_t;
int sc = sizeof(structc_t);

#include  <stddef.h>
int alignof_double = alignof(double);
int c_offset_d = offsetof(structc_t, d);
alignof_double:
    .long   8
c_offset_d:
    .long   4