Assembly MOVNTSS的性能损失是多少?

Assembly MOVNTSS的性能损失是多少?,assembly,x86,sse,cpu-cache,Assembly,X86,Sse,Cpu Cache,要在具有24KB 6路集合关联数据缓存的CPU上对[0,220]中的数字进行基数排序,如果选择base 210,则每个数字只能提供24B缓存,因此此代码将导致大量缓存未命中: int *x[1024], c[1024]={0}; for(int i=0; i<n; i++)c[A[i]&1023]++; for(int i=0,s=0; i<1024; i++){x[i]=B+s; s+=c[i];} for(int i=0; i<n; i++)*(x[A[i]&am

要在具有24KB 6路集合关联数据缓存的CPU上对[0,220]中的数字进行基数排序,如果选择base 210,则每个数字只能提供24B缓存,因此此代码将导致大量缓存未命中:

int *x[1024], c[1024]={0}; 
for(int i=0; i<n; i++)c[A[i]&1023]++;
for(int i=0,s=0; i<1024; i++){x[i]=B+s; s+=c[i];}
for(int i=0; i<n; i++)*(x[A[i]&1023]++)=A[i]; // each ptr require 64B+ cache
int*x[1024],c[1024]={0};

对于(int i=0;i
movntss
仅为AMD(SSE4A),从K10开始支持。但是,在推土机系列和Ryzen上,它比
movntps
慢。(Ryzen的
movntps xmm
每4c吞吐量一个,而Ryzen的
movntps每1c吞吐量一个)

(来自整数寄存器)的吞吐量与AMD Pilledriver(2c)、Steamroller(1c)和Ryzen(1c)上的
MOVTPS xmm
相同。
moventi
是SSE2的一部分,因此它在英特尔CPU上可用(且高效)

您的数字是整数(并且您需要在整数寄存器中使用低位作为数组索引),因此如果要使用NT存储进行此操作,您应该使用
movinti
而不是
movntss


在具有24KB 6路设置关联数据缓存的CPU上

具有SSE2的所有CPU都有较大的L2高速缓存,需要考虑的是L2命中比RAM快得多。 这是一个非常独特的大小。您有一个顺序为(或)的24kiB L1D和至少512 KiB二级缓存(每个核心或在一对corse之间共享)

但无论如何,根本不是AMD,所以movss从来都不是一个选项。AMD的低功耗/捷豹有普通的32k L1d缓存,他们的主流内核有64kiB()、16kiB(推土机系列)或32kiB(Ryzen)L1d缓存,并且都有更大的L2缓存


更重要的是,写回L1d+L2缓存将有效地为输出存储桶提供写组合。我认为您根本不需要NT存储。

你确实需要你的
int*x[]
数组在L1d中保持热状态,因为你在循环中读写它。但是我认为这通常会发生在普通的LRU缓存算法中


NT存储非常糟糕,输出流太多。在刷新行填充缓冲区之前可以存储一个完整的缓存线时,NT存储是最棒的,如果内存子系统需要它来处理从L1d进出的其他行,就会发生这种情况

在主流Intel上,自Nehalem以来,每个内核都有10个LFB(使用超线程,它们在内核之间共享,但如果是静态分区(如存储缓冲区)或竞争性共享(如L1d本身),则为IDK)

在主流内核(关于Atom/Silvermont的IDK)上,NT存储在将缓存线转移到内存子系统()的外部级别之前具有更高的延迟,但是避免RFO可能是一个优势。您必须进行测量

我最担心的是,如果您的数据中有任何模式导致多个不完全连续的存储到同一个存储桶中,这将是非常糟糕的。L1d可能已经吸收的模式可能会非常糟糕,因为NT存储在下一个存储加入写入组合缓冲区之前刷新


因此,此代码将导致大量缓存丢失

您最好执行两个过程;第一个过程使用足够少的存储桶,使输出存储桶在缓存中大部分时间保持热状态(至少如果您倾斜它们,使它们不会全部命中缓存中的同一组)


然后分别对每个存储桶进行排序;理想情况下,它将适合L1d缓存。

MOVNTSS是AMD SSE4a指令集的一部分,首先在K10中实现,并由所有后来的AMD处理器支持。Intel从未支持SSE4a。MOVNTSS执行从XMM寄存器到内存的32位NT存储。SSE4a还提供MOVNTSD,这是一种来自X处理器的64位NT存储每个物理核心的LFB为MM.10。但是,如果该核心是超线程的,则LFB是静态分区的(IIRC),因此每个逻辑核只有5个LFB。对于Atom,在《英特尔优化手册》的D.2中提到,内存执行子系统有8个WC缓冲区。如手册2.5.4中所述,较旧的UARCH(如英特尔核心)也有8个填充缓冲区。我现在找不到一个说它们是静态分区的来源。但为什么es Intel说在3.6.10中只有四个写组合缓冲区保证可以同时使用?我认为这表明它们在启用超线程时是静态分区的,并且还考虑只有8个LFB的UARCH。@HadiBrais:是的,所以它非常适合测试资源是否是静态分区的通过与程序自身拥有整个内核的正常情况进行比较(顺便说一句,有一个
cpu\u clk\u thread\u unhalted.one\u thread\u active
perf counter可用于验证您的测试在整个时间内都拥有内核)如果LFB是静态分区的,而不是竞争性共享的,我会非常惊讶。这对某些超线程场景是一个巨大的打击,我不清楚为什么有必要这么做。x数组中的指针是否指向(大部分)顺序位置?@HadiBrais我不太清楚其分布情况只有在虚拟地址空间中的存储大部分是顺序存储时,您才能从NT存储中获益。您的处理器每个逻辑核有5个写组合缓冲区,每个缓冲区只能容纳一条缓存线。如果顺序写入不到同一缓存线,则内核必须等待要释放WC缓冲区,即使它只是部分写入。这需要直接写入主存,速度非常慢。如果您有类似“可能的顺序访问”,那么使用NT存储可能是一个好主意。总的来说,我不能确定。您必须测量它。@HadiBrais Do NT store buffer需要等待[写入成功]是否准备好进行下一次写入?