伪共享

在计算机科学中，伪共享（）是一种会导致性能下降的使用模式，它可能出现在具有分布式缓存且维持缓存一致性的系统中。当系统参与者尝试定期访问未被另一方更改的数据，但欲访问的数据与正被更改的另一数据共享同一缓存块时，缓存协议可能会强制第一个参与者重新加载整个缓存块，尽管在逻辑上这么做是不必要的。 缓存系统不知道此块内的活动，所以强制第一个参与者承担资源的真共享访问（true shared access）所需的缓存系统开销。

 多处理器CPU缓存 
目前为止，该术语最常见的用法是在现代多处理器CPU缓存中，其中内存以 2的较小的幂个字（例如，64个对齐、连续的字节）为单位的缓存行的形式进行高速缓存。如果两个处理器对同一内存地址区域中可存储在单行的独立数据进行操作，系统中的高速缓存一致性机制可能每次数据写入时都要将整行数据通过总线或互连传输，除了浪费系统带宽外，还引发内存停顿。在某些情况下，消除伪共享可以带来若干数量级的性能改进。 伪共享是自动同步缓存协议的固有产物，尽管也可能存在于分布式文件系统或数据库等环境中，但目前仅在RAM缓存中比较常见。

 例子 
#include <thread>
#include <new>
#include <atomic>

using namespace std;

constexpr bool FALSE_SHARING = true;
constexpr size_t
#if defined(__cpp_lib_hardware_interference_size)
	CACHE_LINE_SIZE = hardware_destructive_interference_size,
#else
	CACHE_LINE_SIZE = 64,
#endif
	SECOND_ALIGN = FALSE_SHARING ? sizeof(int) : CACHE_LINE_SIZE;

using atomic_type = atomic<int>;

struct shared_or_not
{
	atomic_type a alignas(CACHE_LINE_SIZE);
	atomic_type b alignas(SECOND_ALIGN);
};

int main()
{
	shared_or_not sharedOrNot;
	auto theThread = []( atomic_type &atomicValue )
	{
		for( size_t r = 100'000'000; r--; )
			++atomicValue;
	};
	jthread
		threadA( theThread, sharedOrNot.a ),
		threadB( theThread, sharedOrNot.b );
}此 C++20 代码显示了伪共享的影响。shared_or_not 的第一个成员 a 始终对齐到缓存行的开头，以防止数据结构跨越缓存行，从而造成 a 和 b 被意外拆分，而无法演示伪共享的效果。第二个成员 b 直接放置在同一个缓存行中的 a 之后，或者在下一个缓存行上对齐，具体取决于 FALSE_SHARING 设置为 true 还是 false。如果 FALSE_SHARING 设置为 false，代码运行速度通常会快好几倍。

 缓解措施 
有一些方法可以缓解伪共享的影响。例如，可以通过重新排序变量或在变量之间添加填充（未使用的字节）来防止CPU缓存中的伪共享。但是，其中一些更改可能会增加对象的大小，从而导致内存使用量增加。 编译时数据转换也可以缓解伪共享。 然而，其中一些转换可能并不总是被允许的。例如，C++ 23 的 C++ 编程语言标准草案要求对数据成员进行布局时必须使后面的成员具有更高的地址。 

市面上已有检测伪共享的工具。  还有一些系统可以检测和修复执行程序中的伪共享。然而，这些系统会产生一些执行开销。  

 参考资料 

 外部链接 

 Easy Understanding on False Sharing 
 C++ today blog, False Sharing hits again! 
 Dr Dobbs article: Eliminate False Sharing 
 Be careful when trying to eliminate false sharing in Java 

Category:電腦記憶體
Category:快取一致性