什么是_GLOBAL__sub_I_main?使用OpenMP时其耗时过高是什么原因?
问题解答
两个函数的核心作用
__libc_csu_init:是GNU C库内置的程序前置初始化函数,运行在main函数之前,负责初始化程序依赖的全局运行时环境、调用各编译单元的全局构造函数。_GLOBAL__sub_I_main:是GCC为当前编译单元自动生成的函数,负责执行当前代码中所有全局变量、静态变量的构造逻辑,同时会触发静态链接的第三方组件(比如OpenMP运行时)的初始化回调。
并行版本中两个函数耗时高的原因
- OpenMP运行时初始化开销被归入这两个函数
你使用#pragma omp task开启OpenMP并行后,编译器会自动链接OpenMP运行时库libgomp,这套运行时的初始化工作完全在main函数执行前完成:包括检测CPU核心数、创建后台线程池、初始化任务队列、同步锁等资源,所有这些开销都会被统计到负责初始化流程的__libc_csu_init和_GLOBAL__sub_I_main中。串行版本没有链接OpenMP运行时,自然没有这部分开销。 gprof的多线程统计存在归因误差gprof本身是为串行程序设计的性能统计工具,对多线程程序的支持不完善,子线程执行的耗时很多时候会被错误统计到主线程的初始化函数调用链上,这也是你看到这两个函数占比高达73%的核心原因之一,实际初始化本身的耗时并没有统计值显示的这么夸张,很大一部分是子线程运行时的耗时被错误归因了。
并行版本整体耗时更高的优化方向
你当前的实现给每一层递归都加了#pragma omp task,会生成百万级的细粒度任务,任务调度、同步的开销远远超过了并行计算带来的收益,才会出现并行比串行慢的情况。建议添加粒度控制逻辑:只有当待排序的区间长度大于某个阈值(比如1024个元素)时才创建并行任务,小于阈值时直接走串行递归,可大幅降低调度开销。
内容的提问来源于stack exchange,提问作者codertryer
相关产品推荐
相关产品推荐

