自编教材分享:第九章—避免伪共享



分析伪共享
OpenMP在多核处理器间进行同步时常常需要共享一些变量,如用多线程同时对一个数组初始化时,多个线程对同一个数组进行修改,即使线程间从算法上并不需要共享变量,但是在实际执行时,若不同线程所需要赋值的地址处于同一个缓存行中,就会引起缓存冲突,严重降低程序性能,这就是伪共享。
下面通过分析伪共享产生的过程来讨论避免伪共享的方法,例如有4个单核处理器,每个都支持超线程技术即可以在一个核心中提供多个逻辑线程,每个线程对一个独立的存储单元进行修改操作,且一个数组中内存地址相邻的元素会优先放入同一个缓存行,此时若各线程操作的存储单元位于相同缓存行,可能会引起冲突导致性能较差,反之若线程操作的存储单元在不同缓存行中,则可以避免冲突。

数据填充避免伪共享
对数值计算求π程序并行执行时,每个线程计算积分的一部分,之后对计算结果进行汇总,将双精度浮点变量result更改为线程数大小的数组来为每个线程提供空间存储计算结果,并在并行区结束后使用for循环相加汇总所有线程的部分结果

对数值计算求π程序并行执行时,每个线程计算积分的一部分,之后对计算结果进行汇总,将双精度浮点变量result更改为线程数大小的数组来为每个线程提供空间存储计算结果,并在并行区结束后使用for循环相加汇总所有线程的部分结果。

归约操作是指反复地将运算符作用在一个变量或一个值上,并把结果保存在原变量中。归约子句reduction就是对前后有依赖的循环进行归约操作的并行化,即对一个或多个变量指定一个操作符,每个线程将创建变量列表中变量的一个私有副本,并将各线程变量的私有副本进行初始化。
在并行过程中,各线程通过指定的运算符进行归约计算,不断更新各子线程的私有变量副本。在区域结束处,各线程私有变量副本通过指定的运行符运算后,更新原始变量,最后由主线程将归约子句变量列表中的变量值传出并行区。
使用归约子句reduction避免伪共享问题,不同于数据填充进行边界对齐的方式,代码中不再将result声明为数组而是声明为普通变量,使用reduction子句会使得每个线程都有一个变量result的副本,各个线程在对变量result进行写入时不会产生数据冲突,因为result不是共享变量,在各个线程完成写入操作后,使用reduction子句将所有变量result副本进行求和到一个最终的result变量中,该值就是π数值求解的最终结果,避免了数据在缓存行中相互竞争而产生的伪共享问题。

