Intel64及IA-32架构优化指南第8章多核与超线程技术——8.6 存储器优化
8.6 存储器优化
高效的Cache操作是存储器优化的关键方面。Cache的高效操作需要注意以下几点:
● Cache分块
● 共享存储器优化
● 消除64K字节混叠的数据访问
● 防止过多的L1 Cache逐出
8.6.1 Cache分块技术
循环分块对于减少Cache失败并提升存储器访问性能来说是很有用的。当应用循环分块技术时,选择一个合适的块大小是关键。循环分块可应用于单线程应用以及运行在支持或不支持HT技术的处理器上的多线程应用。该技术将存储器访问模式转换为高效命中目标Cache大小的块。
当目标指向支持HT技术的Intel处理器时,对一个统一Cache的循环分块技术可以选择一个不大于目标Cache尺寸一半的块大小,如果有两个逻辑处理器共享那个Cache。用于循环分块的块大小的上限应该通过用目标Cache大小除以一个物理处理器包中可用的逻辑处理器的个数来确定。一般,某些Cache行需要访问不作为在Cache分块中所使用的源或目的缓存部分的数据,这样块大小可以在目标Cache大小的四分之一到二分之一之间来选择(见第3章)。
软件可以使用CPUID的确定性Cache参数来发现逻辑处理器的哪些子集在共享一个所给的Cache(见第7章)。从而,上述准则可以被扩展来允许所有受一个给定Cache服务的逻辑处理器来同时使用Cache,通过将块大小设置为Cache的总大小除以受那个Cache服务的逻辑处理器的个数的上限。这个技术也可以被应用于用作为多线程工作负荷一部分的单线程应用。
用户/源代码编写规则31:使用Cache分块来提升数据访问的位置性。当目标定为支持HT技术的Intel处理器时,或将目标定为一个块大小,允许所有被一个Cache服务的所有逻辑处理器同时共享那个Cache时,将块大小定为Cache大小的四分之一到二分之一。
8.6.2 共享存储器优化
频繁地在独立的处理器之间维护Cache一致性涉及到贯穿一条总线搬运数据,而这是以比处理器频率更低很多的时钟频率进行操作的。
8.6.2.1 在物理处理器之间最小化数据共享
当两个线程在两个物理处理器上执行并共享数据时,读或写共享数据往往涉及到几种总线事务(包括窥探、所有权改变的请求、以及有时跨总线获取数据)。访问一个大量共享存储器的一个线程可能会有糟糕的处理器性能增益。
用户/源代码编写规则32:最小化执行在共享一条公共总线上的不同总线代理的线程之间的数据共享。由多个总线域所组成的一个平台的情况也应该最小化跨总线域的数据共享。
最小化数据共享的一个技术是将数据拷贝到本地栈变量中,如果此数据以一个长期的周期被重复地访问的话。如果有必要,由多个线程所产生的结果可以通过将它们写回到一个共享的存储器位置来稍后联合。这个方法也可以最小化时间花费来同步对共享的数据的访问。
8.6.2.2 批处理的生产者-消费者模型
一个线程化的生产者-消费者设计的关键利益是,在生产者与消费者之间使用一个共享的L2 Cache最小化总线交通。在一个Intel Core Duo处理器上并且当工作缓存足够小,以至于能适应在L1 Cache中时,对生产者和消费者任务的重新安排对于达成最佳性能是有必要的。这是因为从L2取数据到L1比起让一个Cache行在一个核心中无效化然后再从总线去取要快得多。
图8-5描述了一个批处理的生产者-消费者模型,该模型可以用于克服在一个生产者-消费者模型中使用小工作缓存的缺陷。在一个批处理的生产者和消费者模型中,每个调度量批处理两个或更多的生产者任务,每个生产者在一个指派的缓存上工作。要批处理的任务的数量通过总的工作集比L1 Cache大,但比L2 Cache小的标准来确定。
例8-8展示了生产者和消费者线程函数的批处理实现。
例8-8:生产者消费者线程的批处理实现
void producer_thread() { int iter_num = workamount - batchsize; int mode1; for(mode1 = 0; mode1 < batchsize; mode1++) produce(buffs[mode1], count); while(tier_num--) { Signal(&signal1, 1); produce(buffs[mode1], count); // 占位符函数 WaitForSignal(&end1); mode1++; if(mode1 > batchsize) mode1 = 0; } } void consumer_thread() { int mode2 = 0; int iter_num = workamount - batchsize; while(iter_num--) { WaitForSignal(&signal1); consume(buffs[mode2], count); // 占位符函数 Signal(&end1, 1); mode2++; if(mode2 > batchsize) mode2 = 0; } for(i=0; i<batchsize; i++) { consume(buffs[mode2], count); mode2++; if(mode2 > batchsize) mode2 = 0; } }
8.6.3 消除64K字节混叠的数据访问
64K字节混叠情况在第3章中有所讨论。满足64K字节混叠条件的存储器访问会导致L1数据Cache过度的逐出。消除64K字节混叠数据访问一般而言起源于每个线程帮助提升频率校准。此外,它允许L1数据Cache高效执行,当HT技术被软件应用充分利用时。
用户/源代码编写规则33:最小化数据访问模式,在每个线程中的偏移为64K字节的倍数。
可以通过使用奔腾4处理器性能监测事件来探测是否存在64K字节混叠的数据访问。附录B包含了奔腾4处理器性能度量标准的一个更新的列表。这些度量基于使用Intel VTune性能分析器的事件访问。
与64K字节混叠相关联的性能处罚主要对当前实现HT技术的处理器或Intel NetBurst微架构可应用。下一个章节讨论了对运行在支持HT技术的处理器上的多线程应用可应用的存储器优化技术。