golang协程调度 - 线程数设置越多,资源利用率越低,执行时间无差别,为什么?

1. 运行环境

system:CentOS Linux release 7.9.2009 (Core)
CPU: CPU(s): 96; Thread(s) per core:2 ;Core(s) per socket:24 ;Socket(s): 2
memory: 150G+
disk: 8T 不知详细属性的磁盘阵列,读写测试结果
golang调度的cuda加速离线计算任务,计算资源无法充分利用的疑惑,求思考方向

gcc: gcc version 9.3.1 20200408 (Red Hat 9.3.1-2) (GCC)
go: go version go1.16.13 linux/amd64

2. 问题描述?

我遇到的是一个协程调度问题。下面描述我项目的概况,并说明我做的一些测试。
我的输入样例为15g压缩文件,对压缩文件的读取,使用开源项目htslib,该压缩文件的格式由该开源项目定义。生产环境中可能遇到的数据从10g-100g不等。
为了增加并行度,从业务逻辑的层面将整个数据拆分为N份,当前测试文件会被拆分至4600+份,我们把每一份称为一个“区间树”,每一棵区间树单独读取,单独计算,所有树的计算结果输出到同一个文件的不同行中。区间树并行调度的方式如图:

golang协程调度 - 吃不满资源的问题

在一颗区间树中,计算分三个阶段:串行读取 - 并行计算 - 串行合并。
第一个阶段为读取文件,读取的方案如下,读取的单位为read(业务逻辑上的一条序列),读取的方法是使用htslib的C代码,通过cgo调用。reads在逻辑上是成对存在的,下游的计算也需要配对后进行。配对的逻辑是对象中有相同的id。配对依靠golang原生map。一棵树可能存在1k-10w条reads,配对的两条reads可能距离很近,也可能相差上万条read才能配对成功。并行多棵树计算时,reads对流出配对map的速度不稳定。
第二阶段是并行计算。每一对上述配对好的对象单独开一个协程进行计算(协程的数量在千万级,使用ants线程池完成线程复用)。
线程池的初始化:

golang协程调度 - 吃不满资源的问题
任务的提交:

golang协程调度 - 吃不满资源的问题

第三阶段是串行合并。
//: <> (代码问题的话,请提供一份最短的,可复现代码。或相关代码。)

3. 您期望得到的结果?

如果io是阻塞原因,整个程序执行时间应该约等解析时间
如果cpu是阻塞原因,cpu利用率应该长期保持满载状态

4. 您实际得到的结果?

测试得到以下数据:
只执行第一阶段,读取后不做计算,32棵树并行,耗时200s
32棵树并行,线程池Size=64,cpu利用率70%,耗时
32棵树并行,线程池size=1024,cpu利用率60%,耗时

DPeng
讨论数量: 2

我猜测你的瓶颈已经不在cpu了,而是硬盘,就好像我们在windows真机上拷贝文件,只拷贝一个大文件可能是300MB/s,同时拷贝10个大文件可能就变成200MB/s了,因为多线程读写是有性能损失的

1年前 评论
DPeng (楼主) 1年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!