SSE指令:哪些CPU可以执行原子16B内存操作?

考虑x86 CPU上的单个内存访问(单个读取或单个写入,而不是读写)。该指令正在访问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++) {

                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);


桃花长相依
浏览 837回答 3
3回答

守候你守候我

在《英特尔®64和IA-32架构开发人员手册》中。3A如今包含您提到的内存订购白皮书的规格,在第8.2.3.1节中说,正如您自己指出的那样,Intel-64内存排序模型可确保以下各项&nbsp;内存访问指令,似乎要执行组成内存操作&nbsp;作为单个内存访问:•读取或写入单个字节的指令。•读取或写入地址(2字节)对齐的字(2个字节)的指令字节边界。•读取或写入地址对齐的双字(4个字节)的指令在4个字节的边界上。•读取或写入地址对齐在其上的四字(8字节)的指令8字节边界。任何锁定的指令(XCHG指令或其他读-修改-写&nbsp;带有LOCK前缀的指令)似乎作为不可分割的和&nbsp;不间断的装载顺序,然后是存储顺序,与对齐方式无关。现在,由于上面的列表不包含双四字(16字节)的相同语言,因此该体系结构不保证访问16字节内存的指令是原子的。话虽如此,最后一段确实暗示了一条出路,即带有LOCK前缀的CMPXCHG16B指令。您可以使用CPUID指令确定处理器是否支持CMPXCHG16B(“ CX16”功能位)。在相应的AMD文档《AMD64技术AMD64体系结构程序员手册》第2卷:系统编程中,我找不到相似的清晰语言。编辑:测试程序结果(修改了测试程序,使#迭代次数增加了10倍)在Xeon X3450(x86-64)上:0000 999998139 15720001 0 00010 0 00011 0 00100 0 00101 0 00110 0 00111 0 01000 0 01001 0 01010 0 01011 0 01100 0 01101 0 01110 0 01111 1861 999998428在Xeon 5150(32位)上:0000 999243100 2830870001 0 00010 0 00011 0 00100 0 00101 0 00110 0 00111 0 01000 0 01001 0 01010 0 01011 0 01100 0 01101 0 01110 0 01111 756900 999716913在Opteron 2435(x86-64)上:0000 999995893 19010001 0 00010 0 00011 0 00100 0 00101 0 00110 0 00111 0 01000 0 01001 0 01010 0 01011 0 01100 0 01101 0 01110 0 01111 4107 999998099这是否意味着Intel和/或AMD保证16字节内存访问在这些计算机上是原子的?恕我直言,事实并非如此。它不在文档中作为保证的体系结构行为,因此无法知道在这些特定处理器上16字节内存访问是否确实是原子的,或者测试程序是否仅由于某种原因未能触发它们。因此依靠它是危险的。编辑2:如何使测试程序失败哈!我设法使测试程序失败。在与上述相同的Opteron 2435上,具有相同的二进制文件,但是现在通过“ numactl”工具运行它,指定每个线程在单独的套接字上运行,我得到:0000 999998634 59900001 0 00010 0 00011 0 00100 0 00101 0 00110 0 00111 0 01000 0 01001 0 01010 0 01011 0 01100 0 1不是单个内存访问!1101 0 01110 0 01111 1366 999994009那么,这意味着什么呢?好吧,Opteron 2435可以保证也可以不保证16字节内存访问对于套接字内访问是原子的,但是至少在两个套接字之间的HyperTransport互连上运行的缓存一致性协议不能提供这种保证。编辑3:应“ GJ”请求,用于线程功能的ASM。这是为Opteron 2435系统上使用的GCC 4.4 x86-64版本的线程函数生成的asm:.globl thread2&nbsp; &nbsp; &nbsp; &nbsp; .type&nbsp; &nbsp;thread2, @functionthread2:.LFB537:&nbsp; &nbsp; &nbsp; &nbsp; .cfi_startproc&nbsp; &nbsp; &nbsp; &nbsp; movdqa&nbsp; .LC3(%rip), %xmm1&nbsp; &nbsp; &nbsp; &nbsp; xorl&nbsp; &nbsp; %eax, %eax&nbsp; &nbsp; &nbsp; &nbsp; .p2align 5,,24&nbsp; &nbsp; &nbsp; &nbsp; .p2align 3.L11:&nbsp; &nbsp; &nbsp; &nbsp; movaps&nbsp; x(%rip), %xmm0&nbsp; &nbsp; &nbsp; &nbsp; incl&nbsp; &nbsp; %eax&nbsp; &nbsp; &nbsp; &nbsp; movaps&nbsp; %xmm1, x(%rip)&nbsp; &nbsp; &nbsp; &nbsp; movmskps&nbsp; &nbsp; &nbsp; &nbsp; %xmm0, %edx&nbsp; &nbsp; &nbsp; &nbsp; movslq&nbsp; %edx, %rdx&nbsp; &nbsp; &nbsp; &nbsp; incl&nbsp; &nbsp; n2(,%rdx,4)&nbsp; &nbsp; &nbsp; &nbsp; cmpl&nbsp; &nbsp; $1000000000, %eax&nbsp; &nbsp; &nbsp; &nbsp; jne&nbsp; &nbsp; &nbsp;.L11&nbsp; &nbsp; &nbsp; &nbsp; xorl&nbsp; &nbsp; %eax, %eax&nbsp; &nbsp; &nbsp; &nbsp; ret&nbsp; &nbsp; &nbsp; &nbsp; .cfi_endproc.LFE537:&nbsp; &nbsp; &nbsp; &nbsp; .size&nbsp; &nbsp;thread2, .-thread2&nbsp; &nbsp; &nbsp; &nbsp; .p2align 5,,31.globl thread1&nbsp; &nbsp; &nbsp; &nbsp; .type&nbsp; &nbsp;thread1, @functionthread1:.LFB536:&nbsp; &nbsp; &nbsp; &nbsp; .cfi_startproc&nbsp; &nbsp; &nbsp; &nbsp; pxor&nbsp; &nbsp; %xmm1, %xmm1&nbsp; &nbsp; &nbsp; &nbsp; xorl&nbsp; &nbsp; %eax, %eax&nbsp; &nbsp; &nbsp; &nbsp; .p2align 5,,24&nbsp; &nbsp; &nbsp; &nbsp; .p2align 3.L15:&nbsp; &nbsp; &nbsp; &nbsp; movaps&nbsp; x(%rip), %xmm0&nbsp; &nbsp; &nbsp; &nbsp; incl&nbsp; &nbsp; %eax&nbsp; &nbsp; &nbsp; &nbsp; movaps&nbsp; %xmm1, x(%rip)&nbsp; &nbsp; &nbsp; &nbsp; movmskps&nbsp; &nbsp; &nbsp; &nbsp; %xmm0, %edx&nbsp; &nbsp; &nbsp; &nbsp; movslq&nbsp; %edx, %rdx&nbsp; &nbsp; &nbsp; &nbsp; incl&nbsp; &nbsp; n1(,%rdx,4)&nbsp; &nbsp; &nbsp; &nbsp; cmpl&nbsp; &nbsp; $1000000000, %eax&nbsp; &nbsp; &nbsp; &nbsp; jne&nbsp; &nbsp; &nbsp;.L15&nbsp; &nbsp; &nbsp; &nbsp; xorl&nbsp; &nbsp; %eax, %eax&nbsp; &nbsp; &nbsp; &nbsp; ret&nbsp; &nbsp; &nbsp; &nbsp; .cfi_endproc为了完整起见,.LC3是包含thread2使用的(-1,-1,-1,-1)向量的静态数据:.LC3:&nbsp; &nbsp; &nbsp; &nbsp; .long&nbsp; &nbsp;-1&nbsp; &nbsp; &nbsp; &nbsp; .long&nbsp; &nbsp;-1&nbsp; &nbsp; &nbsp; &nbsp; .long&nbsp; &nbsp;-1&nbsp; &nbsp; &nbsp; &nbsp; .long&nbsp; &nbsp;-1&nbsp; &nbsp; &nbsp; &nbsp; .ident&nbsp; "GCC: (GNU) 4.4.4 20100726 (Red Hat 4.4.4-13)"&nbsp; &nbsp; &nbsp; &nbsp; .section&nbsp; &nbsp; &nbsp; &nbsp; .note.GNU-stack,"",@progbits另请注意,这是AT&T ASM语法,而不是Windows程序员可能更熟悉的Intel语法。最后,这是进行曲=本机,这使GCC更喜欢MOVAPS;但这没关系,如果我使用march = core2,它将使用MOVDQA来存储到x,并且仍然可以重现故障。

慕妹3242003

的“AMD架构程序员手册卷1:应用程序编程”表示在节3.9.1:“ CMPXCHG16B可被用来执行在64位模式的16字节的原子访问(与某些对齐限制)”。但是,没有关于SSE指令的评论。实际上,在4.8.3中有一条注释,即LOCK前缀“与128位媒体指令一起使用时会导致无效操作码异常”。因此,在我看来,AMD处理器不能保证对SSE指令进行原子128位访问是完全结论性的,并且进行原子128位访问的唯一方法是使用CMPXCHG16B。“ 英特尔64和IA-32体系结构软件开发人员手册第3A卷:系统编程指南,第1部分 ”在8.1.1中说:“可以使用多次内存访问来实现访问大于四字的数据的x87指令或SSE指令。 ” 这是非常确定的,ISA不能保证128位SSE指令是原子的。 英特尔文档的第2A卷说CMPXCHG16B:“此指令可以与LOCK前缀一起使用,以允许原子执行该指令。”此外,在这种情况下,CPU制造商尚未发布针对特定CPU型号的128b SSE原子操作的书面保证。
打开App,查看更多内容
随时随地看视频慕课网APP