我的内存块可能非常大(比L2缓存大),有时我必须将它们设置为全零。 memset在串行代码中很好,但是并行代码呢? 如果从并发线程调用memset实际上加快了大型数组的速度,有经验吗? 或者甚至使用简单的openmp并行for循环?
答案 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缓存...
但是,它很可能已经受到主内存带宽的限制;添加更多并行性不太可能改善事物。