SSE指令:哪个CPU可以做primefaces16B内存操作?

考虑x86 CPU上的单个内存访问(单次读取或单次写入,而不是读取+写入)SSE指令。 该指令访问16个字节(128位)的内存,访问的内存位置alignment到16个字节。

文档“英特尔®64架构内存订购白皮书”指出,对于“读取或写入地址在8字节边界上alignment的四字(8字节)的指令”,内存操作似乎作为单个内存访问执行,而不pipe内存types。

问题: 是否存在保证读取或写入16字节边界的16字节(128位)作为单个存储器访问的Intel / AMD / x86 x86 CPU? 是这样的,哪种types的CPU是它(Core2 / Atom / K8 / Phenom / …)? 如果您对这个问题提供了答案(是/否), 还请指定用于确定答案的方法 – PDF文档查找,powershelltesting,mathcertificate或您用来确定答案的任何其他方法。

这个问题涉及到诸如http://research.swtch.com/2010/02/off-to-races.html之类的问题


更新:

我用C语言创build了一个简单的testing程序,你可以在你的电脑上运行。 请在您的Phenom,Athlon,Bobcat,Core2,Atom,Sandy Bridge或任何具有SSE2function的CPU上进行编译和运行。 谢谢。

// Compile with: // gcc -oa ac -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没有通过testing,它实现了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 

在英特尔®64和IA-32架构开发人员手册:卷。 3A ,它现在包含你提到的记忆顺序白皮书的规范,如8.2.3.1节所述,当你注意到自己时

英特尔64内存sorting模型保证,为以下每一个 
内存访问指令,构成内存操作似乎执行 
作为单个内存访问:

 •读取或写入单个字节的指令。
 •读取或写入地址在2上alignment的字(2个字节)的指令
字节边界。
 •读取或写入地址alignment的双字(4个字节)的指令
在一个4字节的边界上。
 •读取或写入地址alignment的四字(8字节)的指令
一个8字节的边界。

任何locking的指令(XCHG指令或另一个读取 - 修改 - 写入
 带有LOCK前缀的指令)似乎以不可分割的forms执行 
负载不可中断的顺序,然后是存储(s),不pipealignment。

现在,由于上面的列表不包含双四字(16字节)的相同语言,因此体系结构不保证访问16字节存储器的指令是primefaces的。

也就是说,最后一段提到了一条出路,即带有LOCK前缀的CMPXCHG16B指令。 您可以使用CPUID指令来确定您的处理器是否支持CMPXCHG16B(“CX16”function位)。

在相应的AMD文档中,我找不到类似的清晰语言。

编辑:testing程序结果

(修改testing程序以使#iterations增加10倍)

在Xeon X3450(x86-64)上:

 0000 999998139 1572
 0001 0 0
 0010 0 0
 0011 0 0
 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 0 0
 1101 0 0
 1110 0 0
 1111 1861 999998428

在至强5150(32位)上:

 0000 999243100 283087
 0001 0 0
 0010 0 0
 0011 0 0
 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 0 0
 1101 0 0
 1110 0 0
 1111 756900 999716913

在Opteron 2435(x86-64)上:

 0000 999995893 1901
 0001 0 0
 0010 0 0
 0011 0 0
 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 0 0
 1101 0 0
 1110 0 0
 1111 4107 999998099

这是否意味着Intel和/或AMD保证在这些机器上的16字节内存访问是primefaces的? 恕我直言,它不。 这不在文档中作为保证的架构行为,因此不能确定在这些特定的处理器上16字节的存储器访问是否真的是primefaces的,或者testing程序是否由于某种原因而不能触发它们。 因此依靠它是危险的。

编辑2:如何使testing程序失败

哈! 我设法使testing程序失败。 在同一个Opteron 2435上面,使用相同的二进制,但现在通过“numactl”工具运行它指定每个线程运行在一个单独的套接字,我得到:

 0000 999998634 5990
 0001 0 0
 0010 0 0
 0011 0 0
 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 0 1没有一个单一的内存访问!
 1101 0 0
 1110 0 0
 1111 1366 999994009

那么这是什么意思? 那么,Opteron 2435可以或不可以保证16字节的内存访问对于intra-socket访问来说是primefaces的,但是至less在两个socket之间的HyperTransport互连上运行的高速caching一致性协议并不能提供这样的保证。

编辑3:ASM的线程function,请求“GJ”。

以下是在Opteron 2435系统上使用的GCC 4.4 x86-64版本的线程函数生成的asm:

 .globl thread2 .type thread2, @function thread2: .LFB537: .cfi_startproc movdqa .LC3(%rip), %xmm1 xorl %eax, %eax .p2align 5,,24 .p2align 3 .L11: movaps x(%rip), %xmm0 incl %eax movaps %xmm1, x(%rip) movmskps %xmm0, %edx movslq %edx, %rdx incl n2(,%rdx,4) cmpl $1000000000, %eax jne .L11 xorl %eax, %eax ret .cfi_endproc .LFE537: .size thread2, .-thread2 .p2align 5,,31 .globl thread1 .type thread1, @function thread1: .LFB536: .cfi_startproc pxor %xmm1, %xmm1 xorl %eax, %eax .p2align 5,,24 .p2align 3 .L15: movaps x(%rip), %xmm0 incl %eax movaps %xmm1, x(%rip) movmskps %xmm0, %edx movslq %edx, %rdx incl n1(,%rdx,4) cmpl $1000000000, %eax jne .L15 xorl %eax, %eax ret .cfi_endproc 

为了完整性,.LC3是包含由thread2使用的(-1,-1,-1,-1)向量的静态数据:

 .LC3: .long -1 .long -1 .long -1 .long -1 .ident "GCC: (GNU) 4.4.4 20100726 (Red Hat 4.4.4-13)" .section .note.GNU-stack,"",@progbits 

另请注意,这是AT&T ASM语法,不是Windows程序员可能更熟悉的英特尔语法。 最后,这是march = native,这使得GCC更喜欢MOVAPS; 但是没关系,如果我使用march = core2,它将使用MOVDQA存储到x,我仍然可以重现失败。

英特尔架构手册第3A卷实际上有一个警告。 第8.1.1节(2011年5月)在保证的primefaces操作部分下:

访问大于四字的数据的x87指令或SSE指令可以使用多个存储器访问来实现。 如果这样的指令存储到存储器中,则一些访问可能完成(写入到存储器),而另一个访问由于架构原因(例如,由于被标记为“不存在”的页表项)导致操作错误。 在这种情况下,即使整个指令导致错误,完成的访问的效果对于软件也是可见的。 如果TLB失效延迟(见第4.10.4.4节),即使所有访问都在同一页面,也可能发生此类页面错误。

因此SSE指令不保证是primefaces的,即使底层架构确实使用单个内存访问(这也是引入内存屏蔽的原因之一)。

结合“Intel优化手册”第13.3节(2011年4月)

AVX和FMA指令不会引入任何新的保证primefaces内存操作。

而SIMD的加载或存储操作都不能保证primefaces性的事实,我们可以得出结论:Intel不支持任何forms的primefacesSIMD(还)。

另外一点,如果内存沿高速caching线或页边界分割(当使用像允许非alignment访问的movdqu这样的东西时),下面的处理器将不会执行primefaces访问,不pipealignment方式如何,但是后来的处理器将(同样来自Intelbuild筑手册):

英特尔®酷睿™2双核处理器,英特尔®凌动™,英特尔酷睿双核,奔腾M,奔腾4,英特尔至强,P6系列,奔腾和英特尔486处理器。 Intel Core 2 Duo,Intel Atom,Intel Core Duo,Pentium M,Pentium 4,Intel Xeon和P6系列处理器

“AMD体系结构程序员手册第一卷:应用程序devise”在第3.9.1节中说:“ CMPXCHG16B可用于在64位模式下执行16字节的primefaces访问(具有一定的alignment限制)”。

但是,对于SSE指令没有这样的评论。 实际上,在4.8.3中有一个注释,LOCK前缀“在与128位媒体指令一起使用时导致无效操作码exception”。 因此,对于我来说,AMD处理器并不保证对SSE指令进行primefaces128位访问,而对128位primefaces进行访问的唯一方法是使用CMPXCHG16B

第 8.1.1节“对Intel 64和IA-32架构软件开发人员手册卷3A:系统编程指南”第1部分说:“访问大于四字的数据的x87指令或SSE指令可以使用多个存储器访问来实现。 “ 这是非常确定的,128位SSE指令不保证primefaces的ISA。 英特尔文档的第2A卷说CMPXCHG16B :“该指令可以与LOCK前缀一起使用,以允许指令被primefaces执行。

此外,在这种情况下,CPU制造商还没有公布针对特定CPU模型的primefaces128b SSE操作的书面保证。

x86 ISA并不保证大于8B的任何东西的primefaces性,因此实现可以自由地实现SSE / AVX支持Pentium III / Pentium M / Core Duo的方式:内部数据处理为64位一半。 一个128位的商店是作为两个64位商店完成的。 在Yonah微架构(Core Duo)中,数据path的高速caching仅为64b。 (来源: Agner Fog's microarch doc )。

更新的实现内部具有更宽的数据path,并将128b指令作为单个操作来处​​理。 Core 2 Duo(conroe / merom)是第一个具有128b数据path的Intel P6下一代微架构。 (关于P4的IDK,但幸运的是它已经足够老了,完全不相关)。

这就是为什么OP发现Intel Core Duo(Yonah)上的128b操作系统不是primefaces操作系统的原因,但是其他海报发现,在Core 2(Merom)之后的Inteldevise中,它们是primefaces操作系统。

这篇关于Merom与Yonah的Realworldtech书写的图表展示了Merom(和P4)中的ALU和L1数据caching之间的128位path,而低功耗的Yonah有64位数据path。 L1和L2caching之间的数据通路在所有3种devise中都是256b。

数据通道宽度的下一个跃升来自英特尔的Haswell,具有256b(32B)AVX / AVX2加载/存储 ,以及L1和L2高速caching之间的64Bytepath。 我期望在Haswell,Broadwell和Skylake中256b的加载/存储是primefaces的,但我没有一个可以testing。 我忘了,如果Skylake在Skylake-EP(服务器版本)中再次扩大了准备AVX512的path,或者AVX512的初始实现可能与SnB / IvB的AVX类似,并且512b加载/存储占用加载/存储端口2个周期。


正如janneb在他出色的实验答案中所指出的那样,多核系统中的套接字之间的高速caching一致性协议可能比在共享最后一级高速cachingCPU中得到的更窄。 对于宽泛的加载/存储来说,没有对primefaces性的体系结构要求,所以devise者可以自由地使它们在套接字中是primefaces的,但是如果方便的话,它们在套接字中是非primefaces的。 IDK为AMD推土机系列或英特尔提供的插槽间逻辑数据path有多宽。 (我说“逻辑”,因为即使数据以较小的块传输,它也可能不会修改caching行直到它被完全接收。)


find关于AMD CPU的类似文章应该能够得出关于128b操作是否是primefaces操作的合理结论。 只是检查指令表是一些帮助:

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核心的伊斯坦布尔CPU,它是K10系列的一部分 ,所以这个单一的m-op – >primefaces结论似乎在一个sockets中是准确的。

英特尔Silvermont的128b加载/存储与一个单一的uop,每时钟吞吐量一个。 这与整数加载/存储相同,所以它可能是primefaces的。

编辑:在过去的两天,我已经在我的三台电脑上做了几个testing,我没有再现任何内存错误,所以我不能更准确地说任何东西。 也许这个内存错误也依赖于操作系统。

编辑:我在Delphi中编程,而不是在C中,但我应该明白C.所以我已经翻译的代码,这里有你的线程程序,其中主要部分是在汇编程序:

 procedure TThread1.Execute; var n :cardinal; const ConstAll0 :array[0..3] of integer =(0,0,0,0); begin for n := 0 to 100000000 do asm movdqa xmm0, dqword [x] movmskps eax, xmm0 inc dword ptr[n1 + eax *4] movdqu xmm0, dqword [ConstAll0] movdqa dqword [x], xmm0 end; end; { TThread2 } procedure TThread2.Execute; var n :cardinal; const ConstAll1 :array[0..3] of integer =(-1,-1,-1,-1); begin for n := 0 to 100000000 do asm movdqa xmm0, dqword [x] movmskps eax, xmm0 inc dword ptr[n2 + eax *4] movdqu xmm0, dqword [ConstAll1] movdqa dqword [x], xmm0 end; end; 

结果: 在我的四核心电脑上没有错误,没有错误在我的双核心PC预期!

  1. 带有Intel Pentium4 CPU的PC
  2. 带有Intel Core2 Quad CPU Q6600的PC
  3. 带有Intel Core2 Duo CPU P8400的PC

你能展示debuger如何看你的线程程序代码吗? 请…

很多答案已经发布到目前为止,因此很多信息已经可用(作为一个副作用很多困惑)。 我想从英特尔手册网站事实硬件保证primefaces操作…

在英特尔最新的nehalem和sandy bridge系列处理器中,保证了对64位边界alignment的四字的读写。

即使未alignment的2,4或8字节读取或写入也保证是primefaces的,只要它们被caching在内存中并适合caching行。

说了这个问题发表在这个问题上的testing通过基于Sandy Bridge的英特尔i5处理器。