wym*_*mli 3 caching go false-sharing
众所周知,使用 pad 使结构独占一个或多个高速缓存行有利于性能。
但是对于什么场景,我们应该添加如下所示的pad来提高性能呢?
这里有一些经验法则吗?
import "golang.org/x/sys/cpu"
var S struct {
_ cpu.CacheLinePad
A string
_ cpu.CacheLinePad
}
Run Code Online (Sandbox Code Playgroud)
我从来都不喜欢“虚假分享”这个词。我认为称之为“不恰当的分享”或“过度分享”会更好。
\n\n\n但是对于什么场景,我们应该添加如下所示的pad来提高性能呢?这里有一些经验法则吗?
\n
规则是:首先测量(基准)。然后,如果在某个地方花费了大量时间,请找出原因。
\n如果您使用的底层软件和硬件坚持以缓慢的方式移动数据,即使有更快的方法可用,“错误共享”也会导致性能问题。通过扭曲您自己的代码,您可以说服软件和/或硬件使用更快的方法。
\n这样做通常会降低您自己的代码的可读性,或者占用更多空间,或者具有其他类似的缺点。确保速度增加的价值超过了该缺点的成本。如果您的软件以相同的速度或更低的速度运行,则不会支付因速度而损坏代码的成本,因此不要这样做。1
\n“错误共享”的常见情况\xe2\x80\x94这就是为什么我不喜欢这个术语\xe2\x80\x94的原因,当某些数据结构中的某些数据可以很好地共享,由多个缓存中的多个CPU使用时,除了某些特定的情况外,会发生这种情况。发生数据项写入(存储操作)时,一个 CPU 会使所有其他 CPU 的缓存失效,因此所有其他 CPU 必须返回主内存或从写入 CPU 重新复制数据。如果写入 CPU 不再影响其他 CPU 对相邻数据项的使用,您描述的“插入填充”技巧会有所帮助,因为这些项尽管在逻辑上相邻(例如,在数组或切片的连续元素中) ),不再占用因写入而失效的单个缓存行。
\n例如,假设我们有一个数据结构,其中有 3 个(或者可能是 7 个)八字节字段,多 CPU 机器中的每个 CPU 都会读取这些字段,最后一个 8 字节字段由这些 CPU 之一(但只有一个)可能会更新。进一步假设该机器上的高速缓存行大小为 32(或可能 64)字节,并且 CPU 本身使用类似MESI或MOESI 高速缓存模型。在这种情况下,写入一个八字节字段的一个 CPU会立即使所有其他 CPU 的缓存中存在的任何共享副本失效。
\n但是,如果将由一个 CPU 写入的特定八字节字段位于其自己的缓存行中,或者至少不在共享缓存行\xe2\x80\x94 中,例如位于单独的数组中\xe2\x80\x94则写入CPU不会使任何共享副本失效;这些在所有 CPU 中都保持在 S(共享)状态。
\n如果编译器可以移动某些数据结构的只读和读/写字段,以便可共享部分从时间上受益于共享,保持可共享,那么您将不需要调整您的自己的代码。Go 与 C 和 C++ 一样,对编译器施加了一些限制,可能会阻止它们在这里进行自己的优化,这意味着您可能必须自己进行优化。
\n但一定要先测量!
\n1这类似于股市赚钱的规则:股票要涨就买。如果没有上涨,就不要买。但至少计算机版本实际上是可以实现的,因为您可以运行原始版本和付费扭曲版本,并看看您付出的代价是否值得。
\n