面向共享高速缓存多核系统的软件技术

celineshi 2006-06-22 02:24:09
随着计算性能趋势由单核向多核处理器迈进,系统架构为企业提供了多种选择,其中,高速缓存是最重要的系统资源之一。 为了简单起见,某些架构选择使末级高速缓存专用于每个内核,而其它一些架构试图让不同内核共享末级高速缓存,以获得更佳性价比以及更优资源分配。

例如,英特尔® 酷睿™ 双核处理器就成为第一款利用英特尔智能高速缓存 (Intel® Smart Cache) 技术在多核环境中使用共享高速缓存架构的处理器之一(参考[4])。这种结构允许两个内核访问整个 2 MB 末级高速缓存(此处是二级高速缓存),进而减少资源可能未被充分利用的情况。这种增强型数据预取逻辑通过在高速缓存请求之前预取数据至二级高速缓存,以提高效率。英特尔智能高速缓存全新节能机制支持二级英特尔智能高速缓存根据需要或在非活动期间,动态地将数据移至系统内存。

这种共享高速缓存架构为软件与系统设计人员带来了令人振奋的全新机会。 本文首先介绍了该架构的整体优势;与为系统每个处理器分别提供高速缓存的架构相比,该架构从总体上而言更为优越,本文亦对此进行了解答。 此外,本文介绍了如何借助软件架构与编程技术充分利用这一全新硬件特性,以帮助用户设计与调节应用,从而达到更佳性能。 如果不能合理运用这些技术,便不能充分利用共享高速缓存架构的优势。

共享高速缓存架构的优势

图 1 所示为共享同一系统总线和系统内存的两个处理器(处理器 0 和处理器 1)。 每个处理器内部有两个 CPU 内核,在共享二级高速缓存的同时,每个内核还有其各自专用的一级高速缓存。 共享高速缓存系统拥有众多优势:


末级高速缓存的高效利用
如果一个内核处于闲置状态,那么其它内核会占用所有共享高速缓存
应避免资源未充分利用的情况
为编程人员带来了灵活性
对于共享高速缓存的多个内核而言,在每个单独内核中运行的线程有更多的共享数据的机会。
一个内核可以为其它内核预/后处理数据
内核间的备选通信机制
降低高速缓存一致性的复杂程度
减少由于共享高速缓存引起的错误共享
与专用高速缓存架构相比,保持一致性的工作负载大为降低
减少数据存储冗余
同一个数据仅需储存一次
减少前端总线流量
借助内核间的高效数据共享,数据请求可在共享高速缓存层中实现,而不必全在系统内存中完成。



图 1 共享高速缓存实例: 双核、双处理器系统

诸如英特尔智能高速缓存的节能特性和预取逻辑特性等硬件优势,请参阅参考 [4]。 本文旨在从软件角度考察这种架构的优势。 本白皮书围绕双核/双处理器系统(图 1)为实例进行讨论。 本例中,共享的末级高速缓存为二级高速缓存。 然而,本文中介绍的优势和软件技术也普遍适用于其它共享高速缓存的系统。 充分利用共享高速缓存面临着诸多挑战,以下几节将对应对这些挑战的方法进行介绍。
处理器关联(affinity)

在多核环境中,有必要对运行具体线程或应用的具体内核进行控制。 如果不对其进行控制,线程/应用可能会被指派到错误的处理器或内核中,造成不必要的性能下降。

将仅仅争用高速缓存的线程(这些线程并不共享任何数据,却需要使用高速缓存)彼此区分,并将其指派到不共享高速缓存的内核中是很有意义的。 另一方面,考虑将数据共享线程指派到共享高速缓存的内核中,甚至将联系紧密的线程指派到同一内核中,也非常重要。

不同的操作系统下控制处理器关联的功能不尽相同。

Windows* 实例:


/* 设置处理器关联 */ BOOL WINAPI SetProcessAffinityMask(Handle hProcess, DWORD_PTR dwProcessAffinityMask);

/* 设置线程关联 */ DWORD_PTR WINAPI SetThreadAffinityMask( Handle hThread,DWORD_PTR dwThreadAffinityMask);



Linux* 实例


/* 针对一项任务获得 CPU 关联 */ extern int sched_getaffinity(pid_t pid, size_t cpusetsize,cpu_set_t *cpuset);

/* 针对一项任务设置 CPU 关联 */ extern int sched_setaffinity (pid_t pid,size_t cpusetsize,const cpu_set_t *cpuset);



高速缓存模块化(数据分块)

高速缓存模块化技术(也称数据分块)试图使数据在被数据循环处理时滞留在高速缓存中。 通过减少不必要的高速缓存流量(获取并清除循环中的相同数据),就能达到更好的高速缓存命中率。

高速缓存模块化技术包括大数据集技术,后者可多次运行。 在整个数据集中选择执行一个操作,接着执行第二个操作,依次类推,假如整个数据集不适于高速缓存,高速缓存中的第一个元素将被清除以使最后一个元素适应。然后,在循环里接下来的反复中,加载新的数据将导致旧数据被清除,这样可能产生多米诺效应,此时,每进行一次循环,整个数据集都需要加载。 如果将大型数据集细分为更小的模块,使所有操作先在每个模块上运行,然后再转到下一个模块,那么整个数据模块就有可能在所有操作后仍留存在高速缓存中。

高速缓存模块化有时要求软件设计人员打破思维定势,虽然这些方法并不是些常用且不易被发现,但这样做却为高速缓存的利用带来了更多优势。



图 2: 借助高速缓存模块化技术,提升高速缓存的命中率

图 2 左边模块所示为:比高速缓存容量更大的一个大型数据集上的数据循环。 为了完成一次循环,在循环开始时预取至高速缓存中的某些数据必须被清除,以使数据的剩余部分得以运行。 为了执行循环过程中新的迭代操作,之前被清除的数据需要重新预取到高速缓存中。 其结果是:产生了更多进出高速缓存的流量,并导致更多的高速缓存请求未命中。

如图 2 右侧所示:流程被分为数据集中更小数据块上的四个数据循环,其中每个数据块的大小都比高速缓存的容量更低。 随之而来的结果是:在每个循环过程中,更小的数据块在高速缓存中留存的时间更久,这就减少了进出高速缓存的数据流量,进而降低了高速缓存请求的未命中率。 此处所述实例可能是最简单的高速缓存模块化技术。 还有许多更先进的高速缓存模块化方案,依具体应用和算法的性质而定。

"保留"方法

保留方法包含:通过让每个线程保留其自已的专用数据来降低到共享数据的访问频率,仅在必要时更新共享数据。



图 3: 保留专用数据,仅在必要时更新共享数据

在图 3A 中,线程 0 和线程 1 频繁更新共享数据,随之而来的结果是:进出共享高速缓存的流量会更多;而在图 3B 中,由于仅在必要时更新,线程对共享数据的访问更为井然有序。 达到此目的的一个方法是保留一份专用数据,以便在共享数据更新之前进行跟踪。 其中一项实例是使用 OpenMP* 简化语句,使每个线程保留一份专用数据,仅在所有线程运行结束之后进行整合。

"延迟"方法

延迟方法通过在其它内核请求到达之前插入一条 wait 语句,一直到数据被推送至共享二级高速缓存之前,来实现对一级高速缓存清除的充分利用。 当两个线程在共享二级高速缓存的两个内核中运行,并共享数据时,可部署该方案来调整性能。

延迟方法通过在其它内核要求到达前插入一条 wait 语句,一直到数据被推送至共享二级高速缓存之前,来实现对一级高速缓存清除的充分利用。 当两个线程在共享二级高速缓存的两个内核中运行,并共享数据时,可部署该方案来调整性能。

............
文字太多了,图也帖不上来,大家感兴趣的话去看原文吧
http://www.intel.com/cd/ids/developer/apac/zho/recent/289648.htm?page=1

...全文
392 2 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
2 条回复
切换为时间正序
请发表友善的回复…
发表回复
celineshi 2006-06-30
  • 打赏
  • 举报
回复
怎么会这么认为呢?
Laney 2006-06-23
  • 打赏
  • 举报
回复
晕倒,这里成C-c, C-v的比赛了

567

社区成员

发帖
与我相关
我的任务
社区描述
英特尔® 边缘计算,聚焦于边缘计算、AI、IoT等领域,为开发者提供丰富的开发资源、创新技术、解决方案与行业活动。
社区管理员
  • 英特尔技术社区
  • shere_lin
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧