什么是正确且可移植的方法,以确保在足够小以适合缓存线的结构中真正共享?仅确保结构足够小就足够了吗?还是也必须在缓存边界上对齐?
例如,假设高速缓存行的大小为64个字节,那么以下内容是否足够?
struct A {
std::uint32_t one;
std::uint32_t two;
};
还是我必须这样做?
struct alignas(std::hardware_constructive_interference_size) A {
std::uint32_t one;
std::uint32_t two;
};
注意:这将始终在堆栈上,因此不需要过度对齐的内存分配。
另一项跟进,是否足以确保不存在虚假共享?
struct A {
public:
alignas(hardware_destructive_interference_size) std::uint32_t one;
alignas(hardware_constructive_interference_size) std::uint32_t two;
};
还是必须这样做(例如说hardware_constructive_interference_size
<hardware_destructive_interference_size
?)
struct A {
public:
alignas(hardware_destructive_interference_size) std::uint32_t one;
alignas(hardware_destructive_interference_size) std::uint32_t two;
};
答案 0 :(得分:4)
在第二变型目前是可以做是最好的。
然而,有对齐到高速缓存行的大小没有100%可移植的方法。常量hardware_constructive_interference_size
和hardware_destructive_interference_size
只是提示。他们是编译器的最佳猜测。最终,您在编译时不知道L1高速缓存行的大小。
但是,在实践中,这通常并不重要,因为对于大多数的体系结构有一个典型的高速缓冲存储器线的大小,如64个字节用于x86
甚至更多,在你的例子小结构一样,它始终是足够的自然对齐结构,以确保它完全是一个高速缓存行内。在您的具体示例中,这意味着
struct alignas(8) A {
std::uint32_t one;
std::uint32_t two;
};
将始终确保真正的共享,而不管在运行时实际L1高速缓存行大小的,条件是该高速缓存行大小为8个字节或更大。 (如果它较小,您将不会真正拥有真正的共享。)
关于后续问题:第二个变体将确保没有错误共享。第一种变体可能会导致错误共享,因为高速缓存行大小实际上可能是hardware_destructive_interference_size
,在这种情况下,您将具有错误共享(假设hardware_constructive_interference_size
<hardware_destructive_interference_size
)。>
但是实际上,hardware_destructive_interference_size
和hardware_constructive_interference_size
对于大多数体系结构将具有相同的价值。鉴于这两个常量都不能为您提供真正的L1缓存行大小,而只是提供了编译时的猜测,因此这有点工程化。