[英]OpenMP task directive slower multithreaded than singlethreaded
我遇到了一个问题,任务指令似乎会随着我拥有的线程越多而减慢代码的执行时间。 现在我已经从我的代码中删除了所有与问题无关的不必要的东西,因为即使对于这段实际上没有做任何事情的精简代码,问题仍然存在。 但是我对这段代码的总体想法是让主线程生成任务供所有其他工作线程执行。
#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;
}
我从运行程序中得到以下 output:
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 个线程似乎对性能造成了最大的影响,速度慢了 10 倍。 现在我知道这个问题围绕着 openMP 任务指令,因为如果我删除 busyWait() function,性能将与上面看到的一样。 但是,如果我还删除了 #pragma omp 任务 header 以及 busyWait() 调用,我不会得到任何减速,因此减速不能取决于线程创建。 我不知道这里的问题是什么。
首先,当启用-O2
或-O3
等优化标志时,编译器可以优化for (int i=0; i < 999; i++){}
循环。 事实上,像 Clang 和 GCC 这样的主流编译器在-O2
中对其进行了优化。 分析未优化的构建是浪费时间,除非您有充分的理由这样做,否则永远不要这样做。
假设您启用了优化,创建的任务将为空,这意味着您正在测量创建许多任务的时间。 问题是创建任务很慢,创建许多任务什么都不做会导致争用,使创建速度更慢。 应仔细调整任务粒度,以免对 OpenMP 运行时造成太大压力。 假设您没有启用优化,那么即使是 999 次迭代的循环也不足以让运行时不受压力(在主流机器上它应该持续不到 1 us)。 任务应至少持续几微秒,以使开销不会成为主要瓶颈。 在核心很多的主流服务器上,至少应该是几十微秒。 为了使开销可以忽略不计,任务应该持续更长时间。 任务调度功能强大但代价高昂。
由于在 OpenMP 运行时使用了受原子和锁保护的共享数据结构,争用往往会随着核心数量的增加而增加。 在 NUMA 系统上,由于NUMA 效应,在使用多个 NUMA 节点时它可能会高得多。 具有 16 个内核的 AMD 处理器通常是具有多个 NUMA 节点的处理器。 使用SMT (每个物理核心多个硬件线程)不会显着加快此操作并给 OpenMP 调度程序和 OS 调度程序增加更多压力,因此在这种情况下使用比核心更多的线程通常不是一个好主意(它可能值得当任务计算工作可以从 SMT 中受益时,例如对于延迟受限的任务,以及当开销很小时)。
有关主流 OpenMP 运行时开销的更多信息,请考虑阅读On the Impact of OpenMP Task Granularity 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.