Windows Via C/C++:用户模式下的线程同步——Cache行

为了提升运行在多处理器上的程序的性能,你应该了解CPU Cache的概念。当CPU从内存中读取某个字节时,它会按Cache中数据块大小(也就是Cache行大小)将一整块数据读到CPU的Cache中。比如Cache数据块大小是32,则内存地址按Cache行大小对齐形成0~31、32~63、……、32n~32(n+1)这样的块,当要读取的字节地址为41时,CPU将把包含该地址的整个内存块(32~63)读入Cache中。用公式表示时,假设Cache行大小为C,待读取的变量地址为Addr,则CPU读取内存块的地址是 (Addr/C)*C~(Addr/C+1)*C。将数据读入Cache后,CPU就可以在速度较高的Cache中操作该值,而不用每次都通过数据总线操作内存。

然而,在多处理器环境下,Cache可能会使内存更新出现一些问题,以其中的某一行为例:

  1. CPU1从内存中读取了一个字节B,导致与其相邻的Cache行大小的字节被读入CPU1的Cache行CL1中
  2. CPU2从内存中读取同样的字节B,这样与其相邻的Cache行大小的字节又被读入到CPU2的Cache行CL2中
  3. CPU1更改了B的值,此时CL1中B的值已被改变,但尚未写入到RAM中
  4. CPU2访问B,问题就出现在这里,CPU2从CL2中读到的B的值和CPU1的CL1中的B值没有同步更新

上面的情形是灾难性的。当然,CPU的设计者们已经从硬件角度解决了这个问题,当某个CPU核心Cache行中的值改变时,其它CPU核心将会得知这一改变并刷新自己的Cache。在上面的第4步中,CPU1会把CL1的内容写入内存,然后CPU2重新从内存中读取数据并写入CL2中。可以看出,Cache在多处理器环境中可能会使系统性能下降。 这一切意味着你应该将代码中的数据按照Cache行大小边界对齐,并把只读(或读操作频繁的)数据和读-写数据分开,把经常同时访问的数据组织在一起。

下面是一个设计比较糟糕的结构:

struct CUSTINFO {
  DWORD 	dwCustomerId;	// Mostly read-only
  int		nBalanceDue;	// Read-write
  wchar_t	szName[100];	// Mostly read-only
  FILETIME	ftLastOrderData;// Read-write
};

可以调用GetLogicalProcessorInformation函数查询当前系统CPU的Cache行大小,具体用法可查阅MSDN。一旦获得了Cache行大小,就可以使用C/C++编译器的__declspec(align(#))对齐变量了,下面是改进后的CUSTINFO结构:

#define CACHE_ALIGN 64

struct __declspec(align(CACHE_ALIGN)) CUSTINFO {
  DWORD dwCustomerId;		// Mostly read-only
  wchar_t szName[100];		// Mostly read-only

  // Force the following memebers to be in a different cache line
  _declspec(align(CACHE_ALIGN))
  int nBalanceDue;		// Read-write
  FILETIME ftLastOrderData;	// Read-write
};

关于__declspec(align(#))指令的细节,请参阅http://msdn2.microsoft.com/en-us/library/83ythb65.aspx

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值