对核间延迟基本没有明显影响,如果您怀疑缓存中丢失的后续加载可能存在任何争用,那么在没有仔细分析的情况下绝对不值得“盲目”使用。
一个常见的误解是需要使用 asm 屏障来使存储缓冲区提交到缓存。事实上,障碍只会让这个核心等待一些已经会自行发生的事情,然后再进行后续加载和/或存储。对于完整的屏障,阻止以后的加载和存储,直到存储缓冲区耗尽。英特尔硬件上存储缓冲区的大小?存储缓冲区到底是什么? https://stackoverflow.com/questions/54876208/size-of-store-buffers-on-intel-hardware-what-exactly-is-a-store-buffer
在过去糟糕的日子里std::atomic
, 编译器障碍是阻止编译器将值保留在中的一种方法寄存器(CPU核心/线程私有,不连贯),但这是编译问题而不是asm。具有非一致性缓存的 CPU 理论上是可能的(其中 std::atomic 需要进行显式刷新以使存储可见),但是实际上,没有实现在具有非一致性缓存的核心之间运行 std::thread https://stackoverflow.com/questions/4557979/when-to-use-volatile-with-multi-threading/58535118#58535118.
如果我不使用栅栏,一个核心需要多长时间才能看到另一个核心的写入? https://stackoverflow.com/questions/51292687/if-i-dont-use-fences-how-long-could-it-take-a-core-to-see-another-cores-write高度相关,我之前至少已经写过几次这个答案。 (但这看起来是一个专门回答这个问题的好地方,而不必纠结于哪些障碍的作用。)
可能会有一些非常小的副作用阻止可能与 RFO 竞争的后续加载(让该核心获得对缓存行的独占访问权以提交存储)。 CPU 总是尝试尽快耗尽存储缓冲区(通过提交到 L1d 缓存)。一旦存储提交到 L1d 缓存,它就对所有其他核心全局可见。 (因为他们是一致的;他们仍然需要提出共享请求......)
如果另一个核心上的负载发生在该存储提交之后,则让当前核心将一些存储数据写回到 L3 缓存(尤其是在共享状态下)可以减少未命中损失。但没有好的方法可以做到这一点。制造冲突 https://stackoverflow.com/questions/54209039/x86-mesi-invalidate-cache-line-latency-issue如果生产者性能除了为下一次读取创建低延迟之外并不重要,那么 L1d 和 L2 中可能会丢失。
On x86, 英特尔特雷蒙 https://fuse.wikichip.org/news/1158/intel-discloses-tremont-a-goldmont-plus-successor/(低功耗Silvermont系列)将介绍cldemote https://github.com/HJLebbink/asm-dude/wiki/CLDEMOTE ()将一行写回到外部缓存,但不会一直写到 DRAM。 (clwb
可能会有所帮助,但确实会迫使存储一直使用 DRAM。此外,Skylake 实现只是一个占位符,其工作方式类似于clflushopt
.)
- 有没有办法编写Intel CPU直接核对核通信代码? https://stackoverflow.com/questions/58741806/is-there-any-way-to-write-for-intel-cpu-direct-core-to-core-communication-code
- 如何强制CPU核心刷新C中的存储缓冲区? https://stackoverflow.com/questions/54067605/how-to-force-cpu-core-to-flush-store-buffer-in-c/54068535#54068535
- x86 MESI 无效缓存线延迟问题 https://stackoverflow.com/questions/54209039/x86-mesi-invalidate-cache-line-latency-issue
-
强制将缓存行迁移到另一个核心 https://stackoverflow.com/questions/57336172/force-a-migration-of-a-cache-line-to-another-core(不可能)
有趣的事实:PowerPC 上的非 seq_cst 存储/加载可以在同一物理核心上的逻辑核心之间进行存储转发,从而使存储对some在其他核心变得全局可见之前all其他核心。据我所知,这是线程不同意所有对象的全局存储顺序的唯一真正的硬件机制。其他线程是否总是以相同的顺序看到对不同线程中不同位置的两个原子写入? https://stackoverflow.com/questions/27807118/will-two-atomic-writes-to-different-locations-in-different-threads-always-be-see/50679223#50679223。在其他 ISA 上,包括 ARMv8 和 x86,可以保证存储同时对所有其他内核可见(通过提交到 L1d 缓存)。
对于负载,CPU 已经将需求负载优先于任何其他内存访问(因为当然执行必须等待它们。)加载之前的屏障只能延迟它。
如果这使它看到它正在等待的存储,而不是“太快”并看到旧的缓存的无聊值,那么这可能恰好是最佳的时机巧合。但通常没有理由假设或预测pause
或在负载之前设置屏障可能是一个好主意。
负载后的障碍物也无济于事。稍后的加载或存储可能能够启动,但无序的 CPU 通常会以最早的优先级执行操作,因此在此加载有机会获得其加载请求之前,后面的加载可能无法填满所有未完成的加载缓冲区发送到核心外(假设由于最近存储了另一个核心而导致缓存未命中。)
我想我可以想象如果这个加载地址暂时没有准备好(指针追逐情况)并且当地址已知时最大数量的非核心请求已经在进行中,那么对以后的障碍会有好处。
几乎可以肯定,任何可能的好处都是不值得的;如果有很多独立于该负载的有用工作可以填满所有非核心请求缓冲区(Intel 上的 LFB),那么它很可能不在关键路径上,并且让这些负载运行可能是一件好事。