从 pthread调用的 OpenMP 代码的性能问题
Performance issue of OpenMP code called from a pthread
我尝试从 I/O 绑定操作异步执行一些计算。为此,我使用了一个 pthread,其中循环使用 OpenMP 并行化。但是,与在 pthread 中执行 I/O 绑定操作或根本不创建 pthread 的情况相比,这会导致性能下降。
演示行为的最小示例(见下文)使用 usleep 来模拟 I/O 绑定任务。该程序的输出为
g++ simple.cpp -O3 -fopenmp
export OMP_PROC_BIND=false
No pthread: 34.0884
Compute from pthread: 36.6323
Compute from main: 34.1188
我不明白为什么在创建的 pthread 中使用 OpenMP 会降低性能。更令我惊讶的是以下行为。
export OMP_PROC_BIND=true
./a.out
No pthread: 34.0635
Compute from pthread: 60.6081
Compute from main: 34.0347
为什么运行时有 2 倍?
源代码如下
#include <iostream>
using namespace std;
#include <unistd.h>
#include <omp.h>
int n = 1000000000;
int rep = 100;
double elapsed;
void compute() {
double t = omp_get_wtime();
double s = 0.0;
#pragma omp parallel for reduction(+:s)
for(int i=0;i<n;i++)
s += 1/double(i+1);
elapsed += omp_get_wtime()-t;
}
void* run(void *threadid) {
compute();
}
void* wait(void* threadid) {
usleep(150e3);
}
int main() {
elapsed = 0;
for(int k=0;k<rep;k++)
compute();
cout << "No pthread: " << elapsed << endl;
elapsed = 0;
for(int k=0;k<rep;k++) {
pthread_t t; void* status;
pthread_create(&t, NULL, run, NULL);
usleep(150e3);
pthread_join(t, &status);
}
cout << "Compute from pthread: " << elapsed << endl;
elapsed = 0;
for(int k=0;k<rep;k++) {
pthread_t t; void* status;
pthread_create(&t, NULL, wait, NULL);
compute();
pthread_join(t, &status);
}
cout << "Compute from main: " << elapsed << endl;
}
首先,一个公平的警告 - 从不受 OpenMP 管理的线程创建并行区域是非标准行为,可能会导致相当不可移植的应用程序。
GNU OpenMP 运行时库 (libgomp) 使用线程池来加速线程团队的创建。池与并行区域的主线程相关联,方法是在 TLS(线程本地存储)中的特殊结构中存储对它的引用。创建并行区域时,libgomp 会查询主线程的 TLS 以查找线程池。如果不存在线程池,即这是该线程第一次创建并行区域,它将创建池。
当您从主线程调用compute()
时,libgomp 会在每次创建新的并行区域时在 TLS 中找到对线程池的引用并使用它,因此生成线程的代价仅在第一次创建并行区域时支付。
当您从第二个线程调用compute()
时,libgomp 无法在 TLS 中找到特殊结构,并创建一个新结构和一个新的线程池。线程终止后,一个特殊的析构函数负责终止线程池。因此,每次调用compute()
时都会创建线程池,这仅仅是因为您在外部循环的每个迭代中生成一个新线程。20 毫秒的开销(100 次迭代相差 2 秒)是此类操作的典型值。
我无法用OMP_PROC_BIND=true
重现这种情况 - 程序的运行与没有它的情况基本相同,甚至由于绑定而更好一点。这可能是特定于您的GCC版本,操作系统和CPU组合的东西。请注意,parallel for
构造将工作量均匀分布在团队的所有线程之间。如果其中一个被延迟,例如,由于必须与其他进程分时其CPU内核,那么它将延迟整个并行区域的完成。使用OMP_PROC_BIND=true
,操作系统调度程序不允许移动线程,它们必须与其他进程的线程分时共享其CPU内核。如果一个这样的外部线程使用大量 CPU,则线程绑定时比未绑定线程时危害更大,因为在后一种情况下,调度程序可能会移动受影响的线程。换句话说,在这种特殊情况下,当所有 OpenMP 线程必须共享除一个内核之外的所有内核时,最好将整个程序延迟 #cores / (#cores - 1)
次,而不是延迟 100%,因为一个绑定线程必须与该外部线程共享其 CPU 内核 50/50。当然,在这种情况下,人们要为昂贵的跨核心移动操作付出代价,但如果外部影响过于具有破坏性,它仍然可以带来更好的利用。
- 在类中使用随机生成器时出现性能问题
- Qt OpenGL 渲染到纹理性能问题
- 剪辑性能问题
- OpenCV - 基本操作 - 性能问题 [模式:发布]
- 使用 #define 进行跟踪日志记录以避免性能问题
- 在 Qt C++ 中在自定义项委托上绘制文本时的性能问题
- std::函数有性能问题,如何避免?
- 使用 const double* const 作为模板参数 - 代码性能问题
- 在C 中读取大型CSV文件性能问题
- MPI 二进制文件 I/O 基本功能和性能问题
- 使用STD :: MAP在数据及其性能问题中查找重复项.我可以预先分配吗?
- C 功能性能问题
- 在C 性能问题中使用Getter返回地图
- 初始化每个班级成员时的性能问题
- 事件(开始,结束),1天的最大事件.C++ 中的性能问题
- 在X64模式下从C /CLI调用MASM PROC会产生意外的性能问题
- OpenGL:MESA3D屏幕上的软件渲染性能问题
- 是包含容器性能问题的STL关联容器
- 任何性能问题都在qt框架中使用了stackedwidget的最大页面数
- SHGetFileInfo性能问题