C 在OpenMP并行代码中,memset并行运行有什么好处吗?
我有相当大的内存块(大于二级缓存),有时我必须将它们全部设置为零。memset在串行代码中很好,但是并行代码呢? 如果从并发线程调用memset实际上加快了大型阵列的速度,有人有经验吗?C 在OpenMP并行代码中,memset并行运行有什么好处吗?,c,performance,openmp,C,Performance,Openmp,我有相当大的内存块(大于二级缓存),有时我必须将它们全部设置为零。memset在串行代码中很好,但是并行代码呢? 如果从并发线程调用memset实际上加快了大型阵列的速度,有人有经验吗? 或者甚至使用简单的openmp并行for循环?嗯,总是有L3缓存 然而,这很可能已经受到主内存带宽的限制;增加更多的并行性不太可能改善情况。HPC中的人通常说,一个线程通常不足以使单个内存链路饱和,网络链路也是如此。是我为您编写的一个快速且肮脏的启用OpenMP的memsetter,它用0填充2 GiB的内存。
或者甚至使用简单的openmp并行for循环?嗯,总是有L3缓存
然而,这很可能已经受到主内存带宽的限制;增加更多的并行性不太可能改善情况。HPC中的人通常说,一个线程通常不足以使单个内存链路饱和,网络链路也是如此。是我为您编写的一个快速且肮脏的启用OpenMP的memsetter,它用0填充2 GiB的内存。下面是使用GCC4.7在不同体系结构上使用不同线程数的结果(报告的多次运行的最大值): GCC 4.7,使用
-O3-mtune=native-fopenmp编译的代码:
四插槽Intel Xeon X7350-配备独立内存控制器和前端总线的前置Nehalem四核CPU
单插座
线程第一次触摸重写
11452.223 MB/s 3279.745 MB/s
21541.130 MB/s 3227.216 MB/s
31502.889 MB/s 3215.992 MB/s
41468.931 MB/s 3201.481 MB/s
(由于线程团队是从头开始创建的,并且操作系统正在将物理页面映射到malloc(3)
保留的虚拟地址空间,因此第一次触摸速度较慢)
一个线程已经使单个CPU NB链路的内存带宽饱和。(NB=北桥)
每个插座1个螺纹
线程第一次触摸重写
111455.603 MB/s 3273.959 MB/s
2824.883 MB/s 5346.416 MB/s
33979.515 MB/s 5301.140 MB/s
44128.784 MB/s 5296.082 MB/s
需要两个线程来饱和NB内存链路的全部内存带宽
八位插槽Intel Xeon X7550-带八位核心CPU的8路NUMA系统(禁用CMT)
单插座
线程第一次触摸重写
11469.897 MB/s 3435.087 MB/s
22801.953 MB/s 6527.076 MB/s
33805.691 MB/s 9297.412 MB/s
44647.067 MB/s 10816.266 MB/s
5159.968 MB/s 11220.991 MB/s
65330.690 MB/s 11227.760 MB/s
至少需要5个线程才能使一条内存链路的带宽饱和
每个插座1个螺纹
线程第一次触摸重写
11460.012 MB/s 3436.950 MB/s
22928.678 MB/s 6866.857 MB/s
34408.359 MB/s 10301.129 MB/s
45859.548 MB/s 13712.755 MB/s
57276.209 MB/s 16940.793 MB/s
68760.900 MB/s 20252.937 MB/s
带宽几乎与线程数成线性关系。根据对单个套接字的观察,可以说,为了使所有八个内存链路饱和,至少需要40个线程,每个套接字分配5个线程
NUMA系统的基本问题是第一次触摸内存策略—内存分配在NUMA节点上,在该节点上执行线程第一次触摸特定页面内的虚拟地址。线程固定(绑定到特定的CPU内核)在这样的系统上是必不可少的,因为线程迁移会导致远程访问,这会更慢。大多数OpenMP运行时都支持pinnig。GCC及其libgomp
具有GOMP\u CPU\u AFFINITY
环境变量,Intel具有KMP\u AFFINITY
环境变量等。此外,OpenMP 4.0还引入了与供应商无关的位置概念
编辑:为完整起见,以下是在配备英特尔Core i5-2557M的MacBook Air上使用1 GiB阵列运行代码的结果(配备HT和QPI的双核Sandy Bridge CPU)。编译器是GCC4.2.1(Apple LLVM构建)
线程第一次触摸重写
12257.699 MB/s 7659.678 MB/s
23282.500 MB/s 8157.528 MB/s
34109.371 MB/s 8157.335 MB/s
44591.780 MB/s 8141.439 MB/s
为什么单线程的速度如此之快?对gdb
的一点探索表明,memset(buf,0,len)
被OS X编译器翻译成bzero(buf,len)
,名为bzero$VARIANT$sse42
的支持SSE4.2的矢量化版本由libc.dylib
提供并在运行时使用。它使用MOVDQA
指令一次将16个字节的内存归零。这就是为什么即使只有一个线程,内存带宽也几乎饱和。使用VMOVDQA
的单线程AVX启用版本可以一次将32个字节归零,可能会使内存链接饱和
这里的重要信息是,有时矢量化和多线程在提高操作速度方面不是正交的。不太可能<缓存外数据上的code>memset可能会受到内存带宽的限制。在NUMA机器上并行运行
memset
(所有MP post-Core2 Intel系统以及所有MP甚至一些UP AMD系统都是NUMA)可能是最难理解的性能杀手,除非以后同一线程只访问他们自己归零的数组部分。不过,还是有行业标准的。抓取线程,编译并使用不同数量的线程运行,以便自己查看。还要注意的是,memset()
在大多数libc
实现中都启用了SIMD,并且已经将内存带宽推到了峰值。感谢您提供这些结果。如何使用taskset
和/或设置GOMP\u CPU\u AFFINITY
变量来控制“1thread/socket”或“1 socket中的所有线程”。如果您安装了hwloc
,它将提供漂亮的hwloc ls