nat*_*ouf 18 c performance openmp
我的内存块可能非常大(比L2缓存大),有时我必须将它们设置为全零.memset在串行代码中很好,但是并行代码呢?如果从并发线程调用memset实际上加快了大型数组的速度,有经验吗?或者甚至使用简单的openmp并行for循环?
Hri*_*iev 21
HPC中的人通常说一个线程通常不足以使单个内存链路饱和,对于网络链接通常也是如此.这是一个快速而又脏的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
Run Code Online (Sandbox Code Playgroud)
(从头开始创建线程团队并且操作系统将物理页面映射到保留的虚拟地址空间时,第一次触摸很慢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
Run Code Online (Sandbox Code Playgroud)
需要两个线程来使NB < - >存储器链路的全部存储器带宽饱和.
Octo-socket Intel Xeon X7550 - 具有octo-core 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
Run Code Online (Sandbox Code Playgroud)
至少需要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
Run Code Online (Sandbox Code Playgroud)
带宽与线程数几乎呈线性关系.基于单插槽观察,可以说至少有40个线程分布为每个插槽5个线程,以便使所有8个内存链路饱和.
NUMA系统的基本问题是第一触摸存储器策略 - 在NUMA节点上分配存储器,其中线程首先触摸特定页面内的虚拟地址.线程固定(绑定到特定CPU核心)对于此类系统至关重要,因为线程迁移会导致远程访问,这种速度较慢.大多数OpenMP运行时都支持pinnig.GCC与它libgomp有GOMP_CPU_AFFINITY环境变量,英特尔有KMP_AFFINITY环境变量等.另外,OpenMP 4.0引入了供应商中立的地方概念.
编辑:为了完整性,以下是使用Intel Core i5-2557M(带有HT和QPI的双核Sandy Bridge CPU)在MacBook Air上运行带有1 GiB阵列的代码的结果.编译器是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
Run Code Online (Sandbox Code Playgroud)
为什么这个高速甚至单线程?对OS X编译器翻译的gdb节目进行一些探索,并且在运行时提供由SSE4.2启用的矢量化版本的名称.它使用该指令一次将16个字节的内存清零.这就是为什么即使使用一个线程,内存带宽也几乎饱和.使用单线程AVX的版本可以同时归零32个字节,并可能使内存链路饱和.memset(buf, 0, len)bzero(buf, len)bzero$VARIANT$sse42libc.dylibMOVDQAVMOVDQA
这里的重要信息是,有时矢量化和多线程在提高操作速度方面并不正交.