多核程序探秘- false sharing及使用vtune验证

作者: softarts11 (3 篇文章) 日期: 六月 10, 2009 在 11:58 上午

多核程序探秘(1) false sharing及使用vtune验证

多核开发中常见的一个问题是false sharing(失效共享),这个问题让我们用一个全新的角度来看待多核程序的编写,这个角度就是硬件的角度。

Intel Core 2 Duo处理器平台上, L2 cache是由两个core共享的,而L1 data cache是分开的,由两个core分别存取。cache line的大小是64 Bytes。当不同的线程同时读写不同的,看起来更不相关的2个变量时,由于这2个变量实际保存在同一条cache line上,从而会暗地里造成cache line的访问冲突而导致潜在的性能损失。例如这段代码:

unsigned char VectorA[10];
unsigned char VectorB[10];

UINT MyThreadProcA( LPVOID pParam )
{
unsigned long long myCounter = 100000000;
while(--myCounter)
{
for (int i=0; i<10; ++i)
{
++VectorA[i];
}
}
return 0; // thread completed successfully
}

UINT MyThreadProcB( LPVOID pParam )
{
unsigned long long myCounter = 100000000;
while(--myCounter)
{
for (int i=0; i<10; ++i)
{
++VectorB[i];
}
}
return 0; // thread completed successfully
}

尽管MyThreadProc[A/B] 是两个不同的线程,访问的也是两个不同的变量,但是,false sharing却真真实实的发生了。当MyThreadProcA更新VectorA[i]的时候,对应的Core A上的cache line同时被更新,变为modified状态,而这个cache line中又保存了VectorB[i]的一份copy,因此,另一个Core B中的cacheline就会变为失效状态(invalid),CPU会不得不通过cache protocol(cache的同步协议)去通知Core B上的cache line同时更新VectorB的数据,这样,尽管MyThreadProcA没有修改VectorB,却会导致MyThreadProcB线程访问VectorB时cache miss增加!而我们知道,cache的访问速度是普通内存的10倍,cache miss增大自然会造成明显的性能下降!

在Core2平台上,可以使用EXT_SNOOP.ALL_AGENTS.HITM 事件来评测false sharing的影响,它监测的是总线(内存总线)传输的情况,如果HITM事件发生,则表明总线上响应端的cache正处于修改状态,这恰恰反映了false sharing问题的根源。

vtune的手册对于EXT_SNOOP.ALL_AGENTS.HITM 的解释的:

This event counts the snoop responses to bus transactions. Responses can be counted separately by type and by bus agent. With the 'THIS_AGENT' mask the event counts snoop responses from this processor to bus transactions sent by this processor. With the 'ALL_AGENTS' mask the event counts all snoop responses seen on the bus.

先看看看看上面这段代码的测量结果吧!

采用sampling测量,EXT_SNOOP.ALL_AGENTS.HITM 发生次数1175次,CPU_CLK 为6373,INST_RETIRED为3796

false sharing的解决也很简单,只要把共享的数据放到不同的cache line中即可,例如,将代码改为:

unsigned char VectorA[100];
unsigned char VectorB[100];

这样,使用的仍然是VectorA[0~9]和VectorB[0~9],VectorA[10~99]则充当了一个pad占位符,把同一条cache line(64bytes)占满。

解决false sharing问题后的测量数据为:

EXT_SNOOP.ALL_AGENTS.HITM 显著降到179次,CPU_CLK 降为1847,由于指令个数没有太大变化,INST_RETIRED为3370。通过程序中内嵌计时函数的方法也能得到接近的结果。

总结,解决false sharing问题的方法:

1. 增大数组元素的间隔使得由不同线程存取的元素位于不同的cache line上
2. 在每个线程中创建全局数组各个元素的本地拷贝,然后结束后再写回全局数组

false sharing是多核程序开发的常见问题,需要引起程序员们的重视。

分类: 博客征文专栏
标签:,

如需了解英特尔软件产品相关的性能和优化选项,请参阅优化注意事项.

 评论 (6)

2009年05月31日 22:57

softarts
softarts总分:
1,225
注册用户
为何我无法上传图片?(没有权限?)
2009年06月15日 08:42

Cheng Lian
Cheng Lian总分:
55
注册用户
cache line(64bytes), 那为什么要定义大小为100的数组呢?
2009年06月15日 19:33

Cheng Lian
Cheng Lian总分:
55
注册用户
core2处理器的cache一致性协议是什么呢?
一个核修改了某cache行,应该是M(modify)态啊,怎么回事I态(Invalid)态呢?
2009年06月15日 20:25

softarts
softarts总分:
1,225
注册用户
to chenglian:
cache protocol 我想应该是MESI protocol,其中有这样的描述:
A cache that holds a line in the Modified state must snoop (intercept) all attempted reads (from all of the other caches in the system) of the corresponding main memory location and insert the data that it holds. This is typically done by forcing the read to back off (i.e. retry later), then writing the data to main memory and changing the cache line to the Shared state.

A cache that holds a line in the Shared state must listen for invalidate or read-for-ownership broadcasts from other caches, and discard the line (by moving it into Invalid state) on a match.
修改的那个cache处于modified态,但另一个core上的cache处于“要被invalidate”的状态,但是不是叫inv alid态,还请深入了解这个的专业人士说说:)
那个100的数组当时纯粹为书写方便,严格讲需要align cache line 的.谢谢

2009年06月18日 03:22


zwjchina
我对此可以说是外行。
说点外行的疑问啊,我以前的理解,CPU的cache应该是cache指令才对,怎么会cache数据呢?
况且,对于指针类型的数据防问,CPU如何知道怎样缓存?
2009年06月29日 23:46

kalven
kalven总分:
490
注册用户
好文章,希望都些这种带有较高理论的文章。

 引用 (0)


 写评论  

欲获得技术支持,请访问软件支持页面.
姓名 (必填)*

电子邮件 (必填,不在本页面显示)*

您的 URL (可选)


评论*