考虑在x86 CPU上进行单个内存访问(单个读取或单个写入,而不是读取或写入)SSE指令.该指令访问16字节(128位)的存储器,访问的存储器位置对齐为16字节.
文档"英特尔®64架构内存订购白皮书"指出,对于"读取或写入地址在8字节边界上对齐的四字(8字节)的指令",内存操作似乎作为单个内存访问执行,而不管记忆类型.
问题:是否存在Intel/AMD/etc x86 CPU,它们保证读取或写入与16字节边界对齐的16字节(128位)作为单个内存访问执行?是这样,它是哪种特定类型的CPU(Core2/Atom/K8/Phenom/...)?如果您对此问题提供答案(是/否),请同时指定用于确定答案的方法 - PDF文档查找,强力测试,数学证明或您用于确定答案的任何其他方法.
此问题涉及http://research.swtch.com/2010/02/off-to-races.html等问题
更新:
我在C中创建了一个可以在您的计算机上运行的简单测试程序.请在您的Phenom,Athlon,Bobcat,Core2,Atom,Sandy Bridge或您碰巧拥有的任何支持SSE2的CPU上编译并运行它.谢谢.
// Compile with:
// gcc -o a a.c -pthread -msse2 -std=c99 -Wall -O2
//
// Make sure you have at least two physical CPU cores or hyper-threading.
#include <pthread.h>
#include <emmintrin.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
typedef int v4si __attribute__ ((vector_size (16)));
volatile v4si x;
unsigned n1[16] __attribute__((aligned(64)));
unsigned n2[16] __attribute__((aligned(64)));
void* thread1(void *arg) {
for (int i=0; i<100*1000*1000; i++) { …Run Code Online (Sandbox Code Playgroud) 有人能给我一个简短而合理的解释,说明为什么编译器为数据结构添加填充以便对齐其成员?我知道它已经完成,以便CPU可以更有效地访问数据,但我不明白为什么会这样.
如果这只是CPU相关的,为什么在Linux中对齐4字节,在Windows中对齐8字节?
我总是听说未对齐的访问很糟糕,因为它们会导致运行时错误并导致程序崩溃或减慢内存访问速度.但是我找不到任何关于它们会减慢速度的实际数据.
假设我在x86上并且有一些(但未知)未对齐访问的共享 - 实际可能的最差减速是什么?如何在不消除所有未对齐访问和比较两个版本代码的运行时间的情况下估算它?
请原谅我,如果你觉得这已被无数次回答,但我需要回答以下问题!
为什么数据必须对齐(在4字节/ 8字节/ 2字节边界上)?这里我怀疑的是当CPU具有地址线Ax Ax-1 Ax-2 ... A2 A1 A0时,很可能顺序地寻址存储器位置.那么为什么需要在特定边界对齐数据呢?
在编译代码和生成可执行代码时如何找到对齐要求?
如果例如数据对齐是4字节边界,那是否意味着每个连续字节位于模4偏移处?我怀疑的是,如果数据是4字节对齐,那意味着如果一个字节是1004那么下一个字节是1008(或1005)?
我正在玩这个答案的代码,稍微修改一下:
BITS 64
GLOBAL _start
SECTION .text
_start:
mov ecx, 1000000
.loop:
;T is a symbol defined with the CLI (-DT=...)
TIMES T imul eax, eax
lfence
TIMES T imul edx, edx
dec ecx
jnz .loop
mov eax, 60 ;sys_exit
xor edi, edi
syscall
Run Code Online (Sandbox Code Playgroud)
没有lfence我,我得到的结果与答案中的静态分析一致.
当我介绍一个单一 lfence我期望的CPU执行imul edx, edx的序列的第k个平行于迭代imul eax, eax的下一个(的序列K + 1个)迭代.
像这样的东西(调用一个的imul eax, eax序列和d的imul edx, edx一个): …
我正在查看为我的代码生成的程序集(使用Visual Studio 2017),并注意到_mm_load_ps经常(总是?)编译为movups.
我正在使用_mm_load_ps的数据定义如下:
struct alignas(16) Vector {
float v[4];
}
// often embedded in other structs like this
struct AABB {
Vector min;
Vector max;
bool intersection(/* parameters */) const;
}
Run Code Online (Sandbox Code Playgroud)
现在当我使用这个结构时,会发生以下情况:
// this code
__mm128 bb_min = _mm_load_ps(min.v);
// generates this
movups xmm4, XMMWORD PTR [r8]
Run Code Online (Sandbox Code Playgroud)
我期待movaps因为alignas(16).在这种情况下,我还需要其他东西来说服编译器使用movaps吗?
编辑:我的问题与这个问题不同,因为我没有遇到任何崩溃.结构是专门对齐的,我也使用对齐分配.相反,我很好奇为什么编译器将_mm_load_ps(对齐内存的固有内容)切换到movups.如果我知道struct是在一个对齐的地址分配的,我通过这个*调用它,那么使用movaps是安全的,对吧?
在答案中,我已经声明未对齐访问的速度与对齐访问的速度几乎相同(在x86/x86_64上).我没有任何数字来支持这个陈述,所以我已经为它创建了一个基准.
你看到这个基准测试有什么缺陷吗?你可以改进它(我的意思是,增加GB /秒,所以它更好地反映了真相)?
#include <sys/time.h>
#include <stdio.h>
template <int N>
__attribute__((noinline))
void loop32(const char *v) {
for (int i=0; i<N; i+=160) {
__asm__ ("mov (%0), %%eax" : : "r"(v) :"eax");
__asm__ ("mov 0x04(%0), %%eax" : : "r"(v) :"eax");
__asm__ ("mov 0x08(%0), %%eax" : : "r"(v) :"eax");
__asm__ ("mov 0x0c(%0), %%eax" : : "r"(v) :"eax");
__asm__ ("mov 0x10(%0), %%eax" : : "r"(v) :"eax");
__asm__ ("mov 0x14(%0), %%eax" : : "r"(v) :"eax");
__asm__ ("mov 0x18(%0), %%eax" : : "r"(v) :"eax"); …Run Code Online (Sandbox Code Playgroud) x86 ×4
alignment ×3
performance ×3
assembly ×2
sse ×2
atomic ×1
benchmarking ×1
c ×1
c++ ×1
concurrency ×1
intrinsics ×1
memory ×1
perf ×1
processor ×1
x86-64 ×1