假设我拥有defacto标准x86 CPU,具有3级缓存,L1 / L2私有,并且核心之间共享L3。有没有办法分配共享内存,其数据不会缓存在L1 / L2私有缓存上,而是只缓存在L3?我不想从内存中获取数据(这太昂贵了),但我想尝试使用和不将共享数据带入私有缓存中的性能。
假设L3在核心之间共享(可能是物理索引缓存),因此不会对频繁使用的共享数据产生任何错误共享或缓存行无效。
任何解决方案(如果存在)都必须以编程方式完成,使用C和/或基于intel的CPU组件(相对现代的Xeon架构(skylake,broadwell),运行基于Linux的操作系统。
编辑:
我有延迟敏感代码,它使用一种共享内存形式进行同步。数据将在L3中,但在读取或写入时,将根据缓存包含性策略进入L1 / L2。 通过该问题的暗示,数据必须被无效,增加了不必要的(我认为)性能命中。我想看看是否可以通过一些页面策略或仅在L3中的特殊指令来存储数据。
我知道出于安全原因可以使用特殊内存寄存器来禁止缓存,但这需要CPL0权限。
EDIT2:
我正在处理在高性能系统上运行数月的并行代码。这些系统是高核心数系统(例如40-160 +核心),可定期执行需要在usecs中执行的同步。
答案 0 :(得分:3)
您无法找到阻止对Intel CPU使用L1或L2的好方法:实际上,除了一些特定的场景之外,例如Peter answer中涵盖的UC内存区域(因为他们不使用L3,所以会杀死你的表现,特别是L1从根本上涉及读写。
然而,您可以使用L1和L2的相当明确定义的缓存行为来强制驱逐您只想生活在L3中的数据。在最近的英特尔架构中,L1和L2都表现为伪LRU"标准关联"缓存。通过"标准关联"我指的是您在维基百科或您的硬件101 course中读取的缓存结构,其中缓存被分为2 ^ N个集合,其中M
个条目(M
-way associative cache)和来自地址的N
个连续位用于查找集合。
这意味着您可以准确预测哪些缓存行将在同一个集合中结束。例如,Skylake有一个8路32K L1D和一个4路256K L2。这意味着分开的高速缓存行64K将落入L1和L2上的相同集合中。通常,大量使用的值属于同一个缓存行是一个问题(缓存集争用可能会使您的缓存看起来比它实际上小得多) - 但在这里你可以利用它来发挥你的优势!
如果要从L1和L2中逐出一条线,只需将8个或更多值读取或写入距离目标线64K的其他线。根据您的基准测试(或底层应用程序)的结构,您可能甚至不需要虚拟写入:在您的内部循环中,您可以简单地使用使用16个值,所有值均为64K,并且不会返回到第一个值,直到您我们访问了另一个。这样,每一行都会自然而然地#34;在你使用它之前被驱逐。
请注意,虚拟写入不必在每个核心上相同:每个核心都可以写入"私有"虚线,这样你就不会为虚拟写入添加争用。
一些并发症:
其他一些说明:
perf
公开的效果计数器来确定您实际在L1与L2与L3之间的击球频率,以确保您的技巧有效。答案 1 :(得分:2)
x86无法通过L1D / L2而不是L3来绕过或写入存储。有NT商店绕过所有缓存。任何强制回写L3的东西也会强制回写到内存。 (例如clwb
指令)。这些是针对非易失性RAM用例或非连贯DMA设计的,其中将数据提交到实际RAM非常重要。
也无法绕过L1D进行负载(除了USWC内存使用SSE4.1 movntdqa
,但在其他内存类型上并不“特殊”)。根据英特尔的优化手册,prefetchNTA
可以绕过L2。
执行读取的内核上的预取对于触发从其他内核回写到L3并转移到您自己的L1D中非常有用。但是,只有在您想要执行加载之前准备好地址时,这才有用。 (它有几十个周期才有用。)
Intel CPU使用共享的包含 L3缓存作为片上缓存一致性的后盾。 2插槽必须窥探另一个插槽,但支持2P以上的Xeon有窥探过滤器来跟踪移动的缓存线。
当您读取最近由另一个核心编写的行时,它在您的L1D中始终无效。 L3是包含标签的,其标签有额外的信息来跟踪哪个核心具有该行。 (即使该行在某个L1D中处于M状态,这也是正确的,这要求它在L3中无效,according to normal MESI。)因此,在您的缓存未命中检查L3标记之后,它会触发对L1具有将其写回L3缓存的行(并且可能直接将其发送到核心而不是想要它)。
Skylake-X(Skylake-AVX512)没有包含L3(它有一个更大的私有L2和一个更小的L3),但它仍然有一个包含标签的结构来跟踪哪个核心有一条线。它还使用网格而不是环网,L3延迟似乎比Broadwell差得多。
可能有用:使用直写缓存策略映射共享内存区域的延迟关键部分。 IDK如果这个补丁进入主线Linux内核,但请参阅this patch from HP: Support Write-Through mapping on x86。 (正常的政策是WB。)
还相关:Main Memory and Cache Performance of Intel Sandy Bridge and AMD Bulldozer,深入研究2插槽SnB的延迟和带宽,以及不同启动状态的缓存行。
有关Intel CPU上的内存带宽的更多信息,请参阅Enhanced REP MOVSB for memcpy,尤其是Latency Bound Platforms部分。 (只有10个LFB限制单核带宽)。
相关:What are the latency and throughput costs of producer-consumer sharing of a memory location between hyper-siblings versus non-hyper siblings?有一些实验结果,一个线程垃圾邮件写入一个位置,而另一个线程读取它。
请注意,缓存未命中本身并不是唯一的效果。你还会从核心负责的错误推测中得到很多machine_clears.memory_ordering
。 (x86的内存模型是强有序的,但实际的CPU推测性地提前加载并在极少数情况下中止,在这种情况下,在加载应该“发生”之前,缓存行变为无效。
答案 2 :(得分:2)
英特尔最近宣布了一项似乎与此问题相关的新指令。该指令称为CLDEMOTE。它将数据从较高级别的缓存移动到较低级别的缓存。 (可能从L1或L2到L3,尽管规格在细节上并不精确。)“这可能会加速其他核心对线路的后续访问......”
答案 3 :(得分:-2)
我相信你不应该(也可能不会)关心,并希望共享内存在L3中。 BTW,user-space C代码在virtual address space中运行,而您的其他核心可能(通常也会)运行其他无关 process。
硬件和MMU(由内核配置)将确保L3被正确共享。
但我想尝试使用和不将共享数据放入私有缓存中来实现性能。
据我所知(非常糟糕)最近的英特尔硬件,这是不可能的(至少不在用户区)。
也许您可能会考虑PREFETCH
机器指令和__builtin_prefetch
GCC内置(与您想要的相反,它会将数据带到更近的缓存中)。请参阅this和that。
您可能对virtual address space感兴趣。见processor affinity。了解约sched_setaffinity(2)。见Real-Time Linux。请参阅sched(7)。
我完全不确定你害怕的性能是否值得注意(我相信在用户空间中无法避免)。
也许您可能会考虑在内核空间中移动敏感代码(因此使用CPL0权限)但这可能需要数月的工作并且可能不值得付出努力。我甚至都不会尝试。
您是否考虑过其他完全不同的方法(例如,在GPCLU的OpenCL中重写它)到您的延迟敏感代码?