我一直在In an OpenMP parallel code, would there be any benefit for memset to be run in parallel?测试代码,我正在观察意外的事情。
我的系统是单插槽Xeon E5-1620,它是一个Ivy Bridge处理器,具有4个物理内核和8个超线程。我使用的是Ubuntu 14.04 LTS,Linux Kernel 3.13,GCC 4.9.0和EGLIBC 2.19。我用gcc -fopenmp -O3 mem.c
当我在链接中运行代码时,它默认为8个线程并给出
Touch: 11830.448 MB/s
Rewrite: 18133.428 MB/s
但是,当我绑定线程并将线程数设置为像这样的物理核心数
时export OMP_NUM_THREADS=4
export OMP_PROC_BIND=true
我得到了
Touch: 22167.854 MB/s
Rewrite: 18291.134 MB/s
触控率增加了一倍!绑定后运行几次总是比重写更快。我不明白这一点。 绑定线程并将其设置为物理核心数后,为什么触摸比重写更快?为什么触控率翻倍?
以下是我使用的代码,未经Hristo Iliev回答修改。
#include <stdio.h>
#include <string.h>
#include <omp.h>
void zero(char *buf, size_t size)
{
size_t my_start, my_size;
if (omp_in_parallel())
{
int id = omp_get_thread_num();
int num = omp_get_num_threads();
my_start = (id*size)/num;
my_size = ((id+1)*size)/num - my_start;
}
else
{
my_start = 0;
my_size = size;
}
memset(buf + my_start, 0, my_size);
}
int main (void)
{
char *buf;
size_t size = 1L << 31; // 2 GiB
double tmr;
buf = malloc(size);
// Touch
tmr = -omp_get_wtime();
#pragma omp parallel
{
zero(buf, size);
}
tmr += omp_get_wtime();
printf("Touch: %.3f MB/s\n", size/(1.e+6*tmr));
// Rewrite
tmr = -omp_get_wtime();
#pragma omp parallel
{
zero(buf, size);
}
tmr += omp_get_wtime();
printf("Rewrite: %.3f MB/s\n", size/(1.e+6*tmr));
free(buf);
return 0;
}
编辑: 如果没有胎面装订但使用四个螺纹,则结果会运行八次。
Touch: 14723.115 MB/s, Rewrite: 16382.292 MB/s
Touch: 14433.322 MB/s, Rewrite: 16475.091 MB/s
Touch: 14354.741 MB/s, Rewrite: 16451.255 MB/s
Touch: 21681.973 MB/s, Rewrite: 18212.101 MB/s
Touch: 21004.233 MB/s, Rewrite: 17819.072 MB/s
Touch: 20889.179 MB/s, Rewrite: 18111.317 MB/s
Touch: 14528.656 MB/s, Rewrite: 16495.861 MB/s
Touch: 20958.696 MB/s, Rewrite: 18153.072 MB/s
编辑:
我在其他两个系统上测试了这段代码,我无法重现它们的问题
i5-4250U(Haswell) - 2个物理核心,4个超线程
4 threads unbound
Touch: 5959.721 MB/s, Rewrite: 9524.160 MB/s
2 threads bound to each physical core
Touch: 7263.175 MB/s, Rewrite: 9246.911 MB/s
四个插槽E7- 4850 - 10个物理内核,每个插槽20个超线程
80 threads unbound
Touch: 10177.932 MB/s, Rewrite: 25883.520 MB/s
40 threads bound
Touch: 10254.678 MB/s, Rewrite: 30665.935 MB/s
这表明将线程绑定到物理内核确实改善了触摸和重写,但触摸比在这两个系统上重写要慢。
我还测试了memset的三种不同变体:my_memset
,my_memset_stream
和A_memset
。
函数my_memset
和my_memset_stream
定义如下。函数A_memset
来自Agner Fog的asmlib。
my_memset结果:
Touch: 22463.186 MB/s
Rewrite: 18797.297 MB/s
我认为这表明问题不在EGLIBC的memset函数中。
A_memset结果:
Touch: 18235.732 MB/s
Rewrite: 44848.717 MB/s
my_memset_stream:
Touch: 18678.841 MB/s
Rewrite: 44627.270 MB/s
查看asmlib的源代码,我看到了用于编写大量内存的非时间存储。这就是my_memset_stream
获得与Agner Fog的asmlib相同带宽的原因。 maximum throughput of this system is 51.2 GB/s。{{3}}。因此,这表明A_memset
和my_memset_stream
获得了大约85%的最大吞吐量。
void my_memset(int *s, int c, size_t n) {
int i;
for(i=0; i<n/4; i++) {
s[i] = c;
}
}
void my_memset_stream(int *s, int c, size_t n) {
int i;
__m128i v = _mm_set1_epi32(c);
for(i=0; i<n/4; i+=4) {
_mm_stream_si128((__m128i*)&s[i], v);
}
}
答案 0 :(得分:0)
从您的数字中可以看出,您的4个绑定线程在2个物理内核上运行,而不是预期的4个物理内核。你能证实这一点吗?它可以解释Touch时间加倍。我不确定在系统上使用超线程时如何强制线程到物理核心。 {我尝试将其添加为一个问题,但没有足够的&#34;声誉&#34;}