C++ 为什么ubuntu 12.04下的OpenMP比串行版本慢

C++ 为什么ubuntu 12.04下的OpenMP比串行版本慢,c++,ubuntu,pthreads,openmp,C++,Ubuntu,Pthreads,Openmp,我已经读了一些关于这个话题的其他问题。 然而,他们并没有解决我的问题 我按照下面的方式编写代码,得到的pthread版本和omp版本都比串行版本慢。我很困惑 在以下环境下编译: Ubuntu 12.04 64bit 3.2.0-60-generic g++ (Ubuntu 4.8.1-2ubuntu1~12.04) 4.8.1 CPU(s): 2 On-line CPU(s) list: 0,1 Thread(s) per core: 1 Vendor

我已经读了一些关于这个话题的其他问题。 然而,他们并没有解决我的问题

我按照下面的方式编写代码,得到的
pthread
版本和
omp
版本都比串行版本慢。我很困惑

在以下环境下编译:

Ubuntu 12.04 64bit 3.2.0-60-generic
g++ (Ubuntu 4.8.1-2ubuntu1~12.04) 4.8.1

CPU(s):                2
On-line CPU(s) list:   0,1
Thread(s) per core:    1
Vendor ID:             AuthenticAMD
CPU family:            18
Model:                 1
Stepping:              0
CPU MHz:               800.000
BogoMIPS:              3593.36
L1d cache:             64K
L1i cache:             64K
L2 cache:              512K
NUMA node0 CPU(s):     0,1
编译命令:

g++-std=c++11./eg001.cpp-fopenmp

#include <cmath>
#include <cstdio>
#include <ctime>
#include <omp.h>
#include <pthread.h>

#define NUM_THREADS 5
const int sizen = 256000000;

struct Data {
    double * pSinTable;
    long tid;
};

void * compute(void * p) {
    Data * pDt = (Data *)p;
    const int start = sizen * pDt->tid/NUM_THREADS;
    const int end = sizen * (pDt->tid + 1)/NUM_THREADS;
    for(int n = start; n < end; ++n) {
        pDt->pSinTable[n] = std::sin(2 * M_PI * n / sizen);
    }
    pthread_exit(nullptr);
}

int main()
{
    double * sinTable = new double[sizen];
    pthread_t threads[NUM_THREADS];
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

    clock_t start, finish;

    start = clock();
    int rc;
    Data dt[NUM_THREADS];
    for(int i = 0; i < NUM_THREADS; ++i) {
        dt[i].pSinTable = sinTable;
        dt[i].tid = i;
        rc = pthread_create(&threads[i], &attr, compute, &dt[i]);
    }//for
    pthread_attr_destroy(&attr);
    for(int i = 0; i < NUM_THREADS; ++i) {
        rc = pthread_join(threads[i], nullptr);
    }//for
    finish = clock();
    printf("from pthread: %lf\n", (double)(finish - start)/CLOCKS_PER_SEC);

    delete sinTable;
    sinTable = new double[sizen];

    start = clock();
#   pragma omp parallel for
    for(int n = 0; n < sizen; ++n)
        sinTable[n] = std::sin(2 * M_PI * n / sizen);
    finish = clock();
    printf("from omp: %lf\n", (double)(finish - start)/CLOCKS_PER_SEC);

    delete sinTable;
    sinTable = new double[sizen];

    start = clock();
    for(int n = 0; n < sizen; ++n)
        sinTable[n] = std::sin(2 * M_PI * n / sizen);
    finish = clock();
    printf("from serial: %lf\n", (double)(finish - start)/CLOCKS_PER_SEC);

    delete sinTable;

    pthread_exit(nullptr);
    return 0;
}
我想知道这是否是我的代码的问题,所以我使用pthread做同样的事情

然而,我完全错了,我想知道这是否是Ubuntu在OpenMP/pthread上的问题

我有一个朋友也有AMD CPU和Ubuntu 12.04,在那里也遇到了同样的问题,所以我可能有理由相信这个问题不仅仅局限于我

如果有人和我有同样的问题,或者对这个问题有一些线索,请提前感谢


如果代码不够好,我运行了一个基准测试,并将结果粘贴到这里:

基准url:


新资料:

我用VS2012在windows上运行了代码(没有pthread版本)

我使用了sizen的1/10,因为windows不允许我在结果如下的地方分配这么大的内存主干:

from omp: 1.004
from serial: 1.420
from FreeNickName: 735 (this one is the suggestion improvement by @FreeNickName)
这是否表明这可能是Ubuntu操作系统的问题



通过使用可在操作系统之间移植的
omp\u get\u wtime
函数可以解决这个问题。请通过
Hristo Iliev
查看答案


关于这个有争议的话题的一些测试,作者是
freeigname

(很抱歉,我需要在Ubuntu上测试它,因为windows是我的朋友之一。”)

--1--从
delete
更改为
delete[]
:(但不带memset)(-std=c++11-fopenmp)

--2--在新的之后立即使用memset:(-std=c++11-fopenmp)

--3--在新的之后立即使用memset:(-std=c++11-fopenmp-march=native-O2)

--4--在new之后立即使用memset,并将Free昵称的版本放在OMP之前,以获得版本:(-std=c++11-fopenmp-march=native-O2)

--5--在new之后立即使用memset,将freeinkname的版本放在OMP之前作为版本,并将
NUM_THREADS
设置为5而不是2(我是双核)


在没有任何信息的情况下,Linux调度程序倾向于在同一个内核上调度进程中的线程,以便它们由相同的缓存和内存总线提供服务。它无法知道您的线程将访问不同的内存,因此不会因为位于不同的内核而受到伤害


使用sched_setaffinity函数将每个线程设置为不同的核心掩码。

警告:答案有争议。下面描述的技巧取决于实现,可能导致性能下降。 尽管如此,它也可能增加它。我强烈建议大家看看对这个答案的评论


这并不能真正回答问题,但如果您改变代码并行化的方式,您可能会得到性能提升。现在你这样做:

#pragma omp parallel for
for(int n = 0; n < sizen; ++n)
    sinTable[n] = std::sin(2 * M_PI * n / sizen);

这样,线程将只初始化一次。

在您的情况下,OpenMP没有任何问题。问题在于你测量时间流逝的方式

使用
clock()
来衡量Linux(以及大多数其他类似Unix的操作系统)上多线程应用程序的性能是一个错误,因为它不返回挂钟(实时)时间,而是返回所有进程线程的累计CPU时间(在某些Unix版本中,甚至是所有子进程的累计CPU时间)。您的并行代码在Windows上显示了更好的性能,因为有
clock()
返回实时而不是累计的CPU时间

防止此类差异的最佳方法是使用便携式OpenMP定时器例程
omp\u get\u wtime()


只是Ubuntu的速度较慢吗?并发并不总能提高性能,如果您的工作负载粒度不合适,您可能会遇到性能下降。@ddriver我的朋友也有一个CentOS,哪个并行版本更快。请仅尝试使用两个线程,这是您的CPU实际拥有的数量。不需要在只有两个线程的系统上运行5个线程。线程很可能在争夺资源,这就解释了drop@ddriver这没用。串行版本仍然是最快的<我在不同的场合更新了性能测试。我想你们会感兴趣的。我使用
time
命令来计算cpu使用率(我只运行pthread版本),它给了我:
来自pthread:20.760000./a.out 19.67s用户1.29s系统164%cpu 12.741总计
如果平台上需要
sched_setaffinity
,那么omp运行时平台支持代码肯定会为您调用它。设置
GOMP\u CPU\u AFFINITY
环境变量,而不是处理不可移植的系统调用,怎么样?我建议您在这个问题上可能走错了路,基准测试结果希望解释代码的任何改进都会导致错误不要卑躬屈膝地影响结果。无论如何,感谢您的建议,但它没有起作用:
来自pthread:20.730000;来自omp:20.800000;从序列号起:20780000;免费昵称:20.850000
是,对不起。如果这是一个纯OpenMP问题,ptheads将比串行版本运行得更快。不过,我的回答可能会帮助您提高
omp
性能,当您处理这种奇怪的行为时,我想,我将把它留在这里。这个答案是不正确的。编译器/运行时将为您分配工作。正如您所演示的那样,用手将其拆分不太可能显示出任何改进,并且使代码更难阅读。@pburka,现在请重新阅读问题。到最后,我倾向于同意
from pthread: 13.491405
from omp: 13.023099
from serial: 20.665132
from FreeNickName: 12.022501
from pthread: 13.996505
from omp: 13.192444
from serial: 19.882127
from FreeNickName: 12.541723
from pthread: 11.886978
from omp: 11.351801
from serial: 17.002865
from FreeNickName: 11.198779
from pthread: 11.831127
from FreeNickName: 11.571595
from omp: 11.932814
from serial: 16.976979
from pthread: 9.451775
from FreeNickName: 9.385366
from omp: 11.854656
from serial: 16.960101
#pragma omp parallel for
for(int n = 0; n < sizen; ++n)
    sinTable[n] = std::sin(2 * M_PI * n / sizen);
    int workloadPerThread = sizen / NUM_THREADS;
    #pragma omp parallel for
    for (int thread = 0; thread < NUM_THREADS; ++thread)
    {
        int start = thread * workloadPerThread;
        int stop = start + workloadPerThread;
        if (thread == NUM_THREADS - 1)
                stop += sizen % NUM_THREADS;
        for (int n = start; n < stop; ++n)
            sinTable[n] = std::sin(2 * M_PI * n / sizen);
    }
double start = omp_get_wtime();
#pragma omp parallel for
for(int n = 0; n < sizen; ++n)
    sinTable[n] = std::sin(2 * M_PI * n / sizen);
double finish = omp_get_wtime();
printf("from omp: %lf\n", finish - start);
struct timespec start, finish;
clock_gettime(CLOCK_REALTIME, &start);
#pragma omp parallel for
for(int n = 0; n < sizen; ++n)
    sinTable[n] = std::sin(2 * M_PI * n / sizen);
clock_gettime(CLOCK_REALTIME, &finish);
printf("from omp: %lf\n", (finish.tv_sec + 1.e-9 * finish.tv_nsec) -
                          (start.tv_sec + 1.e-9 * start.tv_nsec));