c++ OpenMP任务指令多线程比单线程慢

lstz6jyr  于 2023-02-06  发布在  其他
关注(0)|答案(1)|浏览(277)

我曾经遇到过一个问题,我拥有的线程越多,task指令似乎会降低代码的执行时间。现在我已经从代码中删除了所有与问题无关的不必要的东西,因为即使这段精简的代码实际上什么也不做,问题仍然会发生。但我对这段代码的总体想法是,让主线程生成任务,以便所有其他工作线程执行。

#ifndef _REENTRANT 
#define _REENTRANT 
#endif 

#include <vector>
#include <iostream>
#include <random>
#include <sched.h>
#include <semaphore.h>
#include <time.h>
#include <bits/stdc++.h>
#include <sys/times.h>
#include <stdio.h>
#include <stdbool.h>
#include <omp.h>
#include <chrono>

#define MAXWORKERS 16

using namespace std;

int nbrThreads = MAXWORKERS;    //Number of threads

void busyWait() {
    for (int i=0; i < 999; i++){}
}

void generatePlacements() {
#pragma omp parallel
{
    #pragma omp master
    {
    int j = 0;
        while (j < 8*7*6*5*4*3*2) {
            #pragma omp task
            {
              busyWait();
            }
            j++;
        }
    }
}
}

int main(int argc, char const *argv[])
{
    for (int i = 1; i <= MAXWORKERS; i++) {
        int nbrThreads = i;
        omp_set_num_threads(nbrThreads);
        auto begin = omp_get_wtime();
        generatePlacements();
        double elapsed;
        auto end = omp_get_wtime();
        auto diff = end - begin;
        cout << "Time taken for " << nbrThreads << " threads to execute was " << diff << endl;
    }
    return 0;
}

我从运行程序中得到了以下输出:

Time taken for 1 threads to execute was 0.0707005
Time taken for 2 threads to execute was 0.0375168
Time taken for 3 threads to execute was 0.0257982
Time taken for 4 threads to execute was 0.0234329
Time taken for 5 threads to execute was 0.0208451
Time taken for 6 threads to execute was 0.0288127
Time taken for 7 threads to execute was 0.0380352
Time taken for 8 threads to execute was 0.0403016
Time taken for 9 threads to execute was 0.0470985
Time taken for 10 threads to execute was 0.0539719
Time taken for 11 threads to execute was 0.0582986
Time taken for 12 threads to execute was 0.051923
Time taken for 13 threads to execute was 0.571846
Time taken for 14 threads to execute was 0.569011
Time taken for 15 threads to execute was 0.562491
Time taken for 16 threads to execute was 0.562118

最值得注意的是,从6个线程的时间似乎变慢了,从12个线程到13个线程似乎有最大的性能打击,变得whooping慢了10倍。现在我知道这个问题围绕着OpenMP任务指令,因为如果我删除busyWait()函数的性能保持与上面看到的相同,但是如果我同时删除#pragma omp任务头沿着busyWait()调用我没有得到任何减速,所以减速不依赖于线程创建。我不知道这里的问题是什么。

xkrw2x1b

xkrw2x1b1#

首先,当-O2-O3优化标志被启用时,for (int i=0; i < 999; i++){}循环可以由编译器优化。事实上,Clang和GCC等主流编译器在-O2中优化它。分析未优化的构建是浪费时间,除非你有很好的理由,否则永远不要这样做。
假设您启用了优化,创建的任务将为空,这意味着您正在测量创建许多任务所需的时间。问题是创建任务很慢,创建许多不执行任何操作的任务会导致争用,从而使创建速度更慢。应仔细调整任务粒度,以免给OpenMP运行时带来太大压力。假设您未启用优化,那么即使999次迭代的循环也不足以使运行时不处于压力之下(在主流机器上应该持续少于1微秒)。任务应该持续至少几微秒以使开销不成为主要瓶颈。在具有许多核心的主流服务器上,它应该至少是几十微秒。2为了使开销可以忽略,任务应该持续更长的时间。3任务调度是强大的,但昂贵。
由于在OpenMP运行时中使用由原子和锁保护的共享数据结构,争用倾向于随着核心的数量而增长。在NUMA系统上,使用多个NUMA节点时,由于NUMA效应,该值可能会明显更高。具有16个内核的AMD处理器通常是具有多个NUMA节点的处理器。使用SMT(每个物理内核多个硬件线程)不会显著加快此操作的速度,并且会给OpenMP调度程序和OS调度程序增加更多压力,因此在这种情况下,使用比内核更多的线程通常不是一个好主意(当任务计算工作可以受益于SMT时,即例如对于 * 延迟受限 * 任务,并且当开销小时,这是值得的)。
有关主流OpenMP运行时开销的更多信息,请考虑阅读On the Impact of OpenMP Task Granularity

相关问题