在OpenMP并行代码中,memset并行运行会有什么好处吗?

时间:2012-07-20 09:32:46

标签: c performance openmp

我的内存块可能非常大(比L2缓存大),有时我必须将它们设置为全零。 memset在串行代码中很好,但是并行代码呢? 如果从并发线程调用memset实际上加快了大型数组的速度,有经验吗? 或者甚至使用简单的openmp并行for循环?

2 个答案:

答案 0 :(得分:21)

HPC中的人通常说一个线程通常不足以使单个内存链路饱和,对于网络链接通常也是如此。 Here是一个快速而又脏的OpenMP启用的memsetter,我为你写的,用两次2 GiB的内存填充零。以下是使用GCC 4.7在不同体系结构上具有不同线程数的结果(报告的几次运行的最大值):

GCC 4.7,使用-O3 -mtune=native -fopenmp编译的代码:

四插槽Intel Xeon X7350 - 具有独立内存控制器和前端总线的前Nehalem四核CPU

单插座

threads   1st touch      rewrite
1         1452.223 MB/s  3279.745 MB/s
2         1541.130 MB/s  3227.216 MB/s
3         1502.889 MB/s  3215.992 MB/s
4         1468.931 MB/s  3201.481 MB/s

(从头开始创建线程组并且操作​​系统将物理页面映射到malloc(3)保留的虚拟地址空间),第一次触摸很慢

一个线程已经使单个CPU的存储器带宽饱和< - > NB链接。 (NB =北桥)

每个插槽1个线程

threads   1st touch      rewrite
1         1455.603 MB/s  3273.959 MB/s
2         2824.883 MB/s  5346.416 MB/s
3         3979.515 MB/s  5301.140 MB/s
4         4128.784 MB/s  5296.082 MB/s

需要两个线程来使NB的全存储器带宽饱和。记忆链接。

Octo-socket Intel Xeon X7550 - 具有八核CPU的8路NUMA系统(禁用CMT)

单插座

threads   1st touch      rewrite
1         1469.897 MB/s  3435.087 MB/s
2         2801.953 MB/s  6527.076 MB/s
3         3805.691 MB/s  9297.412 MB/s
4         4647.067 MB/s  10816.266 MB/s
5         5159.968 MB/s  11220.991 MB/s
6         5330.690 MB/s  11227.760 MB/s

至少需要5个线程才能使一个内存链路的带宽饱和。

每个插槽1个线程

threads   1st touch      rewrite
1         1460.012 MB/s  3436.950 MB/s
2         2928.678 MB/s  6866.857 MB/s
3         4408.359 MB/s  10301.129 MB/s
4         5859.548 MB/s  13712.755 MB/s
5         7276.209 MB/s  16940.793 MB/s
6         8760.900 MB/s  20252.937 MB/s

带宽与线程数几乎呈线性关系。基于单插槽观察,可以说至少有40个线程分布为每个插槽5个线程,以便使所有8个内存链路都饱和。

NUMA系统的基本问题是第一次触摸内存策略 - 在NUMA节点上分配内存,其中线程首先触摸特定页面内的虚拟地址。线程固定(绑定到特定CPU核心)对于此类系统至关重要,因为线程迁移会导致远程访问,这种速度较慢。大多数OpenMP运行时都支持pinnig。 GCC及其libgomp具有GOMP_CPU_AFFINITY环境变量,英特尔具有KMP_AFFINITY环境变量等。此外,OpenMP 4.0引入了地方的供应商中立概念

编辑:为了完整起见,以下是使用 Intel Core i5-2557M (双核Sandy Bridge)在MacBook Air上运行带有1 GiB阵列的代码的结果具有HT和QPI的CPU)。编译器是GCC 4.2.1(Apple LLVM构建)

threads   1st touch      rewrite
1         2257.699 MB/s  7659.678 MB/s
2         3282.500 MB/s  8157.528 MB/s
3         4109.371 MB/s  8157.335 MB/s
4         4591.780 MB/s  8141.439 MB/s

为什么即使是单线程也能实现这种高速度?使用gdb进行一些探索表明,{X}编译器将memset(buf, 0, len)转换为bzero(buf, len),并且bzero$VARIANT$sse42的名称为libc.dylib的SSE4.2启用的矢量化版本由MOVDQA并在运行时使用。它使用VMOVDQA指令一次将16个字节的内存清零。这就是为什么即使使用一个线程,内存带宽也几乎饱和。使用{{1}}的单线程AVX启用版本可以同时归零32个字节,并可能使内存链接饱和。

这里的重要信息是,有时矢量化和多线程在提高操作速度方面并不正交。

答案 1 :(得分:1)

嗯,总有L3缓存...

但是,它很可能已经受到主内存带宽的限制;添加更多并行性不太可能改善事物。