我有以下代码,它将数据从内存复制到DMA缓冲区:
for (; likely(l > 0); l-=128)
{
__m256i m0 = _mm256_load_si256( (__m256i*) (src) );
__m256i m1 = _mm256_load_si256( (__m256i*) (src+32) );
__m256i m2 = _mm256_load_si256( (__m256i*) (src+64) );
__m256i m3 = _mm256_load_si256( (__m256i*) (src+96) );
_mm256_stream_si256( (__m256i *) (dst), m0 );
_mm256_stream_si256( (__m256i *) (dst+32), m1 );
_mm256_stream_si256( (__m256i *) (dst+64), m2 );
_mm256_stream_si256( (__m256i *) (dst+96), m3 );
src += 128;
dst += 128;
}
Run Code Online (Sandbox Code Playgroud)
这就是gcc程序集输出的样子:
405280: c5 fd 6f 50 20 vmovdqa 0x20(%rax),%ymm2
405285: c5 …Run Code Online (Sandbox Code Playgroud) 在英特尔优化手册似乎对存储缓冲区的数量存在于处理器的许多地方,但谈判没有谈存储缓冲区的大小.这是公共信息还是商店缓冲区的大小保留为微架构细节?
我正在研究的处理器主要是Broadwell和Skylake,但其他人的信息也不错.
另外,存储缓冲区究竟做了什么?
我正在阅读一些论文,他们要么可以互换使用存储缓冲区和存储队列,要么与不同的结构有关,而我无法跟进.这就是我认为的商店队列:
我不确定存储缓冲区是什么,但我认为只是一些缓冲区空间来保持退役存储指令的数据等待写入内存(同样,L1).
现在,这就是为什么我感到困惑.在此论文中,指出"我们提出可扩展存储缓冲器[SSB],这使私人/推测值直接进入L1高速缓存,从而避免不可缩放缔搜索常规存储缓冲器的".我认为他们所讨论的不可扩展的关联式可搜索传统结构就是我所知道的商店队列,因为他们也说
SSB通过将处理器可见/推测值直接转发到L1高速缓存的加载,消除了传统存储缓冲区的不可扩展的关联搜索.
正如我上面提到的,据我所知,数据转发到加载是通过存储队列完成的.在第一页的脚注中,也有人说
我们使用"存储队列"来指代在退役之前保存商店值的存储和"存储缓冲区"以在存储到存储器之前引用包含已退休存储值的存储.
这符合我上面解释的内容,但它与第一个引用中的"存储缓冲区"冲突.脚注对应于论文中的参考文献之一.他们说,在那篇参考文献中
存储缓冲区是存在于许多当前处理器中以实现以下一个或多个的机制:存储访问顺序,延迟隐藏和数据转发.
我再次认为实现这些机制的机制称为存储队列.他们后来在同一篇论文中说
通常使用非阻塞高速缓存和缓冲结构,例如写缓冲区,存储缓冲区,存储队列和加载队列.
因此,他们分别提到存储缓冲区和存储队列,但稍后不再提及存储队列.他们说
存储缓冲区维护存储的顺序,并且只有在完成所有先前的指令之后才允许存储
他们的商店缓冲模型与Mike Johnson的模型相同.在约翰逊的书(超标量微处理器设计)中,商店首先以获取顺序进入商店预订站.从那里,它们被发送到地址单元,并从地址单元被发送到"存储缓冲区"及其相应的数据.通过此存储缓冲区处理加载转发.我再一次认为这个结构被称为商店队列.在参考文献#2中,作者也提到了这一点
Alpha 21264微处理器有一个32项的推测商店缓冲区,商店一直存在,直到它退役."
我看了一篇关于Alpha 21264的论文,其中指出了这一点
商店首先将数据通过数据总线传输到推测性存储缓冲区.商店数据保留在推测商店缓冲区中,直到商店退休.退出后,数据将在空闲缓存周期内写入数据缓存.
也,
内部存储器系统维护一个32项加载队列(LDQ)和一个32项存储队列(STQ),用于管理它们在飞行中的引用.[...] Stores在退出并转储到数据缓存后以获取顺序退出STQ.[...] STQ CAM逻辑控制推测数据缓冲区.当较旧的商店之后发生较年轻的负载时,它可以绕过推测商店数据加载.
因此,听起来像在Alpha 21264中有一个存储队列,它以获取顺序保存有关存储指令的一些信息,但它不保留存储指令的数据.存储指令的数据保存在存储缓冲区中.
所以,在所有这些之后,我不确定存储缓冲区是什么.它只是存储队列的辅助结构,还是存储等待写入L1的数据的完全不同的结构.或者是别的什么?当我们说"存储缓冲区"时,我觉得有些作者的意思是"存储队列".有任何想法吗?
文章" Atomic*.lazySet是单个作者的性能胜利 ",讨论lazySet了弱易失性写入(在某种意义上它充当商店存储而不是存储负载围栏).但我不明白利用半易失性写入如何提高并发队列性能.它如何提供Menta队列声称的超低延迟?
我已经阅读了它的实现,它在堆栈溢出问题上声称:" 如何实现Java的Atomic*类中的lazySet "和" Atomic Integer的lazySet vs set".
由于等待全局状态的全局同步几乎会一直不必要地停止所有执行,因此多处理器系统会无序地执行“实际”内存操作(那些操作会影响最终执行,而不仅仅是推测执行)。另一方面,从每个L1高速缓存开始,从允许的行为角度来看,内存系统似乎是完全同步,一致且平坦的(允许语义)。显然,时间取决于缓存的大小和行为。
因此,在一个CPU上,一个极端被称为“寄存器”,根据定义,它们是私有的,而在另一个极端上,则存在共享的内存。令人遗憾的是,在具有特殊命名或寻址模式的寄存器的微不足道的空间之外,存储器始终是全局的,共享的和全局同步的,并且实际上完全受制于所有限制,即使该存储器用作未命名的寄存器也是如此。其目的是存储比少数寄存器中容纳的数据更多的数据,而不会被其他线程检查(除非使用ptrace进行调试,因为ptrace显然会停止,停止,序列化并存储执行的完整可观察状态)。
在现代计算机(现代=可以合理地支持C ++和Java的计算机)上,情况总是如此吗?
专用L1高速缓存为什么不为那些仅由特定内核使用的存储单元提供类似寄存器的语义?高速缓存必须跟踪共享的内存,无论如何。当需要对内存操作进行严格的全局排序时,不必暂停此类本地数据的内存操作,因为没有其他内核在观察它,并且如果需要,缓存可以暂停此类外部访问。高速缓存将只需要知道哪些存储单元是私有的(不可全局读取),直到出现混乱的操作停顿为止,这将使之保持一致(高速缓存可能需要一种方法来请求核心对操作进行序列化并发布一致的状态在记忆中)。
是否所有CPU都停滞不前并同步篱笆或同步操作上的所有内存访问?
内存可以用作几乎不受限制的寄存器资源吗?
memory cpu-architecture cpu-registers memory-barriers cpu-cache
据我了解,当 CPU 推测性地执行一段代码时,它会在切换到推测性分支之前“备份”寄存器状态,以便如果预测结果错误(使分支无用)——寄存器状态将是安全恢复,而不会破坏“状态”。
所以,我的问题是:推测执行的 CPU 分支是否可以包含访问 RAM 的操作码?
我的意思是,访问 RAM 不是“原子”操作——如果数据当前不在 CPU 缓存中,那么从内存中读取一个简单的操作码可能会导致实际的 RAM 访问,这可能会变成一个非常耗时的操作,从 CPU 的角度来看。
如果在推测分支中确实允许这种访问,它是否仅用于读取操作?因为,我只能假设,如果一个分支被丢弃并执行“回滚”,根据它的大小恢复写操作可能会变得非常缓慢和棘手。而且,可以肯定的是,至少在某种程度上支持读/写操作,因为寄存器本身,在某些 CPU 上,据我所知,物理上位于 CPU 缓存上。
所以,也许更精确的表述是:推测执行的一段代码有什么限制?
超线程技术是英特尔推出的一种同步多线程技术.
这些资源包括执行引擎,缓存和系统总线接口; 资源共享允许两个逻辑处理器更有效地相互协作,并允许停滞的逻辑处理器从另一个逻辑处理器借用资源.
在具有超线程的Intel CPU中,一个CPU内核(具有多个ALU)可以在同一时钟执行来自2个线程的指令.两个线程共享:存储缓冲区,缓存L1/L2和系统总线.
但是如果两个线程在一个Core上同时执行,则thread-1存储原子值,而thread-2加载此值,将用于此交换的内容:共享存储缓冲区,共享缓存L1/L2还是通常的缓存L3?
如果两个线程来自同一个进程(相同的虚拟地址空间)和两个不同进程(不同的虚拟地址空间),会发生什么?
Sandy Bridge Intel CPU - 缓存L1:
低12位 - 对于确定当前设定数值很重要
4 KB - 标准页面大小
performance ×3
assembly ×2
concurrency ×2
x86 ×2
atomic ×1
c ×1
cpu ×1
cpu-cache ×1
gcc ×1
intel ×1
java ×1
memory ×1
optimization ×1
x86-64 ×1