首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在OpenMP并行代码中,memset并行运行有什么好处吗?

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

Stack Overflow用户
提问于 2012-07-20 17:32:46
回答 2查看 3K关注 0票数 18

我的内存块可能非常大(比L2缓存还要大),有时我必须将它们设置为全零。memset在串行代码中很好用,但是并行代码呢?有没有人有过从并发线程调用memset来加速大型数组的经验呢?或者甚至使用简单的openmp并行for循环?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 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四核,带独立内存控制器和前端总线

单插槽

代码语言:javascript
复制
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个线程

代码语言:javascript
复制
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)

单插槽

代码语言:javascript
复制
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个线程

代码语言:javascript
复制
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)

代码语言:javascript
复制
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个字节清零,并且可能会使内存链路饱和。

这里的重要信息是,有时向量化和多线程在提高操作速度方面不是正交的。

票数 24
EN

Stack Overflow用户

发布于 2012-07-20 18:06:03

嗯,总是有L3缓存的……

然而,这很可能已经受到主存带宽的限制;增加更多的并行度不太可能改善情况。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11576670

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档