Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 在OpenMP并行代码中,memset并行运行有什么好处吗?_C_Performance_Openmp - Fatal编程技术网

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

C 在OpenMP并行代码中,memset并行运行有什么好处吗?,c,performance,openmp,C,Performance,Openmp,我有相当大的内存块(大于二级缓存),有时我必须将它们全部设置为零。memset在串行代码中很好,但是并行代码呢? 如果从并发线程调用memset实际上加快了大型阵列的速度,有人有经验吗? 或者甚至使用简单的openmp并行for循环?嗯,总是有L3缓存 然而,这很可能已经受到主内存带宽的限制;增加更多的并行性不太可能改善情况。HPC中的人通常说,一个线程通常不足以使单个内存链路饱和,网络链路也是如此。是我为您编写的一个快速且肮脏的启用OpenMP的memsetter,它用0填充2 GiB的内存。

我有相当大的内存块(大于二级缓存),有时我必须将它们全部设置为零。memset在串行代码中很好,但是并行代码呢? 如果从并发线程调用memset实际上加快了大型阵列的速度,有人有经验吗?
或者甚至使用简单的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