共 1,304 篇文章
共 6,317 篇文章及评论
- Association for Computing Machinery TechNews (ACM)
- Go Parallel! (Dr. Dobbs)
- HPCwire (Tabor Communications, Inc.)
- insideHPC (John West)
- Joe Duffy's Weblog (Microsoft)
- Microsoft Parallel Programming Development Center (Microsoft Germany)
- MultiCoreInfo.com
- scalability.org (Scalable Informatics)
- Software Dev Blog (Intel Germany)
- Soft Talk Blog (Intel United Kingdom)
- The Moth (Microsoft)
多核编程伪共享问题及其对策
作者: Zhouweiming 周伟明 (42 篇文章) 日期: 三月 26, 2009 在 10:26 上午
多核编程中的伪共享问题及其对策
注:本文主要内容摘自笔者所著的《多核计算与程序设计》一书,略有修改,后续还会继续发布系列文章,如有需要,可以考虑将一下地址加入到您的浏览器收藏夹中:http://software.intel.com/zh-cn/blogs/category/multicore/。
前一篇文章 “多核编程锁竞争问题及其对策”中,提到了多核编程所遇到的各种问题,本文就来讲述一种的一个问题,伪共享问题及其对策。
伪共享问题在《多核程序设计技术-通过软件多线程提升性能》一书中有详细讲解,它是由于CPU cache机制造成的,CPU读取Cache时是以行为单位读取的,如果两个硬件线程的两块不同内存位于同一Cache行里,那么当两个硬件线程同时在对各自的内存进行写操作时,将会造成两个硬件线程写同一Cache行的问题,它会引起竞争,就像在乒乓球比赛一样,效率将成百倍的下降。
在单核系统中,伪共享问题是不存在的,因为同一时刻只有一个硬件线程在执行,不存在同时写同一Cache行的问题。
伪共享问题在实际情况中是经常可以碰到的,比如两个线程同时写一个数组的相邻部分,或者写两块相邻的内存,这些都有可能造成伪共享问题。
对于分配的内存,可以采取一定的内存分配算法使各块内存不在同一Cache行里,但对于数组或变量的访问,就必须要由程序员在设计时进行避免伪共享问题。
要解决伪共享问题,首先必须知道给定的内存中,那块区域会处于同一Cache行内,Intel的系统中,有一个简单的算法可以得到一块内存中对应的Cache行首地址,即每个Cache行首地址都是Cache行大小的整数倍。
比如一块内存大小为60字节,首地址为0x0012ff52,由于0x0012ff52除以64以后余数为0x12,因此这个地址不是Cache行的首地址。在这个地址之前的Cache行首地址为0x0012ff40,在这个地址之后的Cache行首地址为0x0012ff80。对应的内存位于两块不同的Cache中。如下图所示:
图1:Cache行对齐示意图
根据上面所说的Cache行首地址为Cache行大小的整数倍的特点,可以设计一个函数来取出给定地址之后的第0个Cache行首地址。代码如下:
/** 计算给定地址之后的第0个Cache行首地址
如果给定地址刚好为一个Cache行首地址,那么计算结果等于它自身
@param void *pAddr - 给定的地址
@return void * - 返回给定地址之后的第0个Cache行首地址
*/
void *GetCacheAlignedAddr(void *pAddr)
{
int m = CACHE_LINE_SIZE;
void *pRet = (void *)((UINT(pAddr + m - 1)) & (-m));
return pRet;
}
取到了Cache行首地址后,就可以区分出两块内存是否在同一Cache行中了,对避免伪共享问题就有了很大的帮助。
如果给定地址刚好等于某个Cache行首地址,但是却想取到的地址是它的下一个Cache行首地址,那么可以用以下函数来获取:
/** 计算给定地址之后的Cache行首地址
如果给定地址刚好为一个Cache行首地址,那么计算结果等于它的下一个Cache行首地址
@param void *pAddr - 给定的地址
@return void * - 返回给定地址之后的Cache行首地址
*/
void *GetNextCacheAlignedAddr(void *pAddr)
{
int m = CACHE_LINE_SIZE;
void *pRet = (void *)(((UINT)(pAddr) + m )&(-m));
return pRet;
}
当然,伪共享问题在许多地方都会出现,各种地方的处理方法各有不同,但是所有的方法都需要用到上面讲过的取Cache行首地址的方法。
伪共享问题遇得较多的地方是处理数组类型的数据,其次是内存管理上要从源头上将伪共享问题减少。后续的多核系列文章中,还会有文章详细讲解具体的伪共享处理实例。
参考文献:
【1】 Capi开源项目,链接:http://gforge.osdn.net.cn/projects/capi
【2】 Intel Thread Building Block(TBB)源代码, http://www.threadingbuildingblocks.org/
【3】 Shameem Akhter, Jason Roberts,“多核程序设计技术-通过软件多线程提升性能”,李宝峰等译,电子工业出版社,2007.03.
【4】 周伟明,“多核编程文章汇总”,http://blog.csdn.net/drzhouweiming/archive/2009/03/24/4019792.aspx
【5】 周伟明,“多核编程中的锁竞争问题及其对策”,http://software.intel.com/zh-cn/blogs/2009/03/26/400001180/
【6】 周伟明,“多核计算与程序设计”,华中科技大学出版社,2009.03.
分类: 博客征文专栏, 并行计算
如需了解英特尔软件产品相关的性能和优化选项,请参阅优化注意事项.
评论 (0)
引用 (19)
- 英特尔® 软件网络博客 - 中文 » 多核中的动态任务调度(1)
2009年05月30日 19:52 - 英特尔® 软件网络博客 - 中文 » 多核中的动态任务调度(2)
2009年05月30日 19:56 - 英特尔® 软件网络博客 - 中文 » 多核中的动态任务调度(3)
2009年05月30日 19:59 - 英特尔® 软件网络博客 - 中文 » 多核中的动态任务调度(4)
2009年05月30日 20:02 - 英特尔® 软件网络博客 - 中文 » 用动态任务调度器实现Parallel_For (1)
2009年05月30日 20:28 - 英特尔® 软件网络博客 - 中文 » 用动态任务调度器实现Parallel_For (2)
2009年05月30日 20:30 - 英特尔® 软件网络博客 - 中文 » 用Parallel_For进行并行快速排序
2009年05月30日 20:43 - 英特尔® 软件网络博客 - 中文 » 多核中的动态任务调度(1)
2009年11月16日 19:02 - 英特尔® 软件网络博客 - 中文 » 用原子操作解决多线程退出问题(3)
2009年11月16日 19:04 - 英特尔® 软件网络博客 - 中文 » 原子操作在多核编程中的使用(2)
2009年11月16日 19:05 - 英特尔® 软件网络博客 - 中文 » 并行顺序搜索及终止检测(3)
2009年11月16日 19:07 - 英特尔® 软件网络博客 - 中文 » 多核编程锁竞争问题及其对策
2009年11月16日 19:07 - 英特尔® 软件网络博客 - 中文 » 多核分布式队列的实现:“偷”与“自私”的运用(4)
2009年11月16日 19:11 - 英特尔® 软件网络博客 - 中文 » 多核编程的四层境界
2009年11月16日 19:13 - 英特尔® 软件网络博客 - 中文 » 多核编程中的条件同步模式
2009年11月16日 19:13 - 英特尔® 软件网络博客 - 中文 » OpenMP编程指南
2009年11月16日 19:15 - TECHPOT » Blog Archive » OpenMP编程指南
2012年01月18日 23:48 - TECHPOT » Blog Archive » OpenMP编程指南
2012年01月26日 07:26 - T客网 ︱ Techpot » Blog Archive » OpenMP编程指南
2012年01月27日 03:27


