我的内存块可能非常大(比L2缓存还要大),有时我必须将它们设置为全零。memset在串行代码中很好用,但是并行代码呢?有没有人有过从并发线程调用memset来加速大型数组的经验呢?或者甚至使用简单的openmp并行for循环?
发布于 2012-07-20 21:04:58
HPC中的人通常会说,一个线程通常不足以饱和单个内存链接,网络链接也是如此。Here是我为您编写的一个支持OpenMP的快速而肮脏的内存设置程序,它在2 GiB的内存中填充了两次零。以下是在不同架构上使用不同线程数的GCC 4.7的结果(报告了几次运行的最大值):
GCC 4.7,使用-O3 -mtune=native -fopenmp编译的代码
Nehalem四插槽英特尔至强X7350 -pre-Nehalem四核,带独立内存控制器和前端总线
单插槽
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存储器链路的全部存储器带宽饱和。
采用八核CPU的八插槽英特尔至强X7550 -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节点上分配内存,首先在NUMA节点上执行第一个接触特定页面内的虚拟地址的线程。线程锁定(绑定到特定的CPU核心)在这样的系统上是必不可少的,因为线程迁移会导致远程访问,而远程访问会更慢。大多数OpenMP运行时都提供了对pinnig的支持。GCC用其libgomp有了GOMP_CPU_AFFINITY环境变量,英特尔有了KMP_AFFINITY环境变量等。另外,OpenMP 4.0引入了厂商中立的位置概念。
编辑:为完整起见,以下是在采用英特尔酷睿i5-2557M(双核沙桥处理器,含HT和QPI)的MacBook Air上运行代码的结果。编译器是GCC 4.2.1 (Apple LLVM build)
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的一些探索表明,memset(buf, 0, len)被OS X编译器转换为bzero(buf, len),并且libc.dylib提供了一个名为bzero$VARIANT$sse42的支持SSE4.2的矢量化版本,并在运行时使用该版本。它使用MOVDQA指令一次将16字节的内存清零。这就是为什么即使只有一个线程,内存带宽也几乎饱和。使用VMOVDQA的单线程AVX版本可以一次将32个字节清零,并且可能会使内存链路饱和。
这里的重要信息是,有时向量化和多线程在提高操作速度方面不是正交的。
发布于 2012-07-20 18:06:03
嗯,总是有L3缓存的……
然而,这很可能已经受到主存带宽的限制;增加更多的并行度不太可能改善情况。
https://stackoverflow.com/questions/11576670
复制相似问题