考虑在x86 CPU上进行单个内存访问(单个读取或单个写入,而不是读取写入)SSE指令 . 该指令访问16字节(128位)的存储器,访问的存储器位置对齐为16字节 .
文档“英特尔®64架构内存订购白皮书”指出,对于“读取或写入地址在8字节边界上对齐的四字(8字节)的指令”,内存操作似乎作为单个内存访问执行,而不管记忆类型 .
问题: Do there exist Intel/AMD/etc x86 CPUs which guarantee that reading or writing 16 bytes (128 bits) aligned to a 16 byte boundary executes as a single memory access? 是这样,它是哪种特定类型的CPU(Core2 / Atom / K8 / Phenom / ...)?如果您对此问题提供答案(是/否), please also specify the method 用于确定答案 - 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++) {
int mask = _mm_movemask_ps((__m128)x);
n1[mask]++;
x = (v4si){0,0,0,0};
}
return NULL;
}
void* thread2(void *arg) {
for (int i=0; i<100*1000*1000; i++) {
int mask = _mm_movemask_ps((__m128)x);
n2[mask]++;
x = (v4si){-1,-1,-1,-1};
}
return NULL;
}
int main() {
// Check memory alignment
if ( (((uintptr_t)&x) & 0x0f) != 0 )
abort();
memset(n1, 0, sizeof(n1));
memset(n2, 0, sizeof(n2));
pthread_t t1, t2;
pthread_create(&t1, NULL, thread1, NULL);
pthread_create(&t2, NULL, thread2, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
for (unsigned i=0; i<16; i++) {
for (int j=3; j>=0; j--)
printf("%d", (i>>j)&1);
printf(" %10u %10u", n1[i], n2[i]);
if(i>0 && i<0x0f) {
if(n1[i] || n2[i])
printf(" Not a single memory access!");
}
printf("\n");
}
return 0;
}
我笔记本电脑中的CPU是Core Duo(不是Core2) . 这个特殊的CPU未通过测试,它实现了16字节的内存读/写,粒度为8字节 . 输出是:
0000 96905702 10512
0001 0 0
0010 0 0
0011 22 12924 Not a single memory access!
0100 0 0
0101 0 0
0110 0 0
0111 0 0
1000 0 0
1001 0 0
1010 0 0
1011 0 0
1100 3092557 1175 Not a single memory access!
1101 0 0
1110 0 0
1111 1719 99975389
6 回答
在Intel® 64 and IA-32 Architectures Developer's Manual: Vol. 3A,现在包含你提到的内存订购白皮书的规格,在8.2.3.1节中说,正如你自己注意到的那样,
现在,由于上面的列表不包含双四字(16字节)的相同语言,因此架构不保证访问16字节内存的指令是原子的 .
话虽如此,最后一段确实提示了一条出路,即带有LOCK前缀的CMPXCHG16B指令 . 您可以使用CPUID指令确定您的处理器是否支持CMPXCHG16B(“CX16”功能位) .
在相应的AMD文档AMD64 Technology AMD64 Architecture Programmer’s Manual Volume 2: System Programming中,我找不到类似的清晰语言 .
EDIT: Test program results
(修改测试程序以将#iterations增加10倍)
在Xeon X3450(x86-64)上:
在Xeon 5150(32位)上:
在Opteron 2435(x86-64)上:
这是否意味着英特尔和/或AMD保证这些机器上的16字节内存访问是原子的?恕我直言,它没有 . 它不是文档中保证的架构行为,因此无法知道在这些特定处理器上16字节内存访问是否真的是原子的,或者测试程序是否因某种原因而无法触发它们 . 因此依赖它是危险的 .
EDIT 2: How to make the test program fail
哈!我设法使测试程序失败 . 在与上面相同的Opteron 2435上,使用相同的二进制文件,但现在通过“numactl”工具运行它,指定每个线程在单独的套接字上运行,我得到:
那么这意味着什么呢?好吧,Opteron 2435可能会或可能不会保证16字节内存访问对于套接字内访问来说是原子的,但至少在两个套接字之间的HyperTransport互连上运行的缓存一致性协议并不能提供这样的保证 .
EDIT 3: ASM for the thread functions, on request of "GJ."
这是为Opteron 2435系统上使用的GCC 4.4 x86-64版本的线程函数生成的asm:
为了完整性,.LC3是包含thread2使用的(-1,-1,-1,-1)向量的静态数据:
另请注意,这是AT&T ASM语法,而不是Windows程序员可能更熟悉的英特尔语法 . 最后,这是march = native,这使得GCC更喜欢MOVAPS;但没关系,如果我使用march = core2它将使用MOVDQA存储到x,我仍然可以重现失败 .
"AMD Architecture Programmer's Manual Volume 1: Application Programming"在第3.9.1节中说:“
CMPXCHG16B
可用于在64位模式下执行16字节原子访问(具有某些对齐限制) . ”但是,没有关于SSE指令的评论 . 实际上,4.8.3中有一条注释,即LOCK前缀"causes an invalid-opcode exception when used with 128-bit media instructions" . 因此,对于我来说,AMD处理器不保证对SSE指令进行原子128位访问,并且进行原子128位访问的唯一方法是使用
CMPXCHG16B
,这似乎是非常确定的 .“Intel 64 and IA-32 Architectures Software Developer’s Manual Volume 3A: System Programming Guide, Part 1 " says in 8.1.1 "一个x87指令或访问大于四字的数据的SSE指令可以使用多个存储器访问来实现 . ”这是128位SSE的结论ISA不保证说明原则 . Volume 2A英特尔文档中提及
CMPXCHG16B
:"This instruction can be used with a LOCK prefix to allow the instruction to be executed atomically."此外,在这种情况下,CPU制造商尚未针对特定CPU型号发布原子128b SSE操作的书面保证 .
英特尔架构手册第3A卷实际上有一个警告 . 第8.1.1节(2011年5月),在保证原子操作部分下:
因此SSE指令不保证是原子的,即使底层架构确实使用单个内存访问(这是引入内存防护的一个原因) .
将其与英特尔优化手册第13.3节(2011年4月)中的声明相结合
而事实上SIMD的加载或存储操作都没有保证原子性,我们可以得出结论,英特尔不支持任何形式的原子SIMD(尚未) .
作为额外的一点,如果内存沿着缓存行或页面边界分割(当使用允许未对齐访问的
movdqu
之类的东西时),以下处理器将不执行原子访问,无论对齐,但后来的处理器将(再次来自英特尔)建筑手册):x86 ISA不保证任何大于8B的原子性,因此实现可以像Pentium III / Pentium M / Core Duo那样自由地实现SSE / AVX支持:内部数据以64位半数处理 . 128位商店作为两个64位商店完成 . 在Yonah微体系结构(Core Duo)中,到/从缓存的数据路径只有64b宽 . (来源:Agner Fog's microarch doc) .
更新的实现在内部具有更宽的数据路径,并且将128b指令作为单个op处理 . Core 2 Duo(conroe / merom)是第一款具有128b数据路径的英特尔P6下行微型计算机 . (IDK关于P4,但幸运的是它已经足够老了,完全无关紧要 . )
这就是OP发现128b操作在英特尔酷睿双核(Yonah)上不是原子的原因,但其他海报发现它们在后来的英特尔设计中是原子的,从核心2(Merom)开始 .
The diagrams on this Realworldtech writeup about Merom vs. Yonah显示Merom(和P4)中ALU和L1数据缓存之间的128位路径,而低功率Yonah具有64位数据路径 . 在所有3种设计中,L1和L2缓存之间的数据路径为256b .
数据路径宽度的下一个跳跃来自Intel的Haswell, featuring 256b (32B) AVX/AVX2 loads/stores,以及L1和L2缓存之间的64Byte路径 . 我希望在Haswell,Broadwell和Skylake中256b加载/存储是原子的,但我没有't have one to test. I forget if Skylake again widened the paths in preparation for AVX512 in Skylake-EP (the server version), or if perhaps the initial implementation of AVX512 will be like SnB/IvB'的AVX,并且有512b加载/存储占用加载/存储端口2个周期 .
正如janneb在他出色的实验答案中指出的那样,多核系统中套接字之间的缓存一致性协议可能比共享最后一级缓存CPU中的套接字更窄 . 对于宽负载/存储,原子性没有架构要求,因此设计人员可以自由地在套接字内使它们成为原子,但如果方便则可以在套接字内使用非原子 . IDK对于AMD的Bulldozer系列或Intel的插槽间逻辑数据路径有多宽 . (我说“逻辑”,因为即使数据以较小的块传输,它也可能不会在完全接收之前修改缓存行 . )
查找有关AMD CPU的类似文章应该可以得出关于128b操作是否是原子的合理结论 . 只需检查指令表是一些帮助:
K8将
movaps reg, [mem]
解码为2 m-ops,而K10和推土机系列将其解码为1 m-op . AMD的低功耗山猫将其解码为2个操作系统,而美洲虎则将128b移动解码为1个操作系统 . (它支持类似于推土机系列CPU的AVX1:256b insn(甚至ALU操作)被分成两个128b操作 . Intel SnB仅拆分256b加载/存储,同时具有全宽ALU . )janneb的Opteron 2435是6-core Istanbul CPU, which is part of the K10 family,所以这个单m-op - >原子结论在单个插槽中看起来很准确 .
英特尔Silvermont使用单个uop进行128b加载/存储,每个时钟的吞吐量为1 . 这与整数加载/存储相同,因此很可能是原子的 .
EDIT: 在过去的两天里,我在我的三台电脑上进行了几次测试,而且我没有更准确地说出任何东西 . 也许这个内存错误也依赖于操作系统 .
EDIT: 我正在使用Delphi进行编程而不是使用C进行编程但是我应该理解C.所以我已经翻译了代码,这里有你的主要部分在汇编程序中的线程程序:
结果: no mistake on my quad core PC and no mistake on my dual core PC as expected!
配备Intel Pentium4 CPU的PC
配备Intel Core2 Quad CPU Q6600的PC
配备Intel Core2 Duo CPU P8400的PC
你能说明debuger如何看待你的线程程序代码吗?请...
到目前为止已经发布了很多答案,因此很多信息已经可用(副作用也很多混乱) . 我想从英特尔手册中找到关于硬件保证原子操作的事实......
In Intel's latest processors of nehalem and sandy bridge family, reading or writing to a quadword aligned to 64 bit boundary is guaranteed.
即使未对齐的2,4或8字节读取或写入也保证是原子的,只要它们是高速缓存的内存并适合高速缓存行 .
已经说过在这个问题中发布的测试通过基于沙桥的intel i5处理器 .