如何计算Task数量来充分利用上CPU

本文探讨了如何计算Task数量以充分利用CPU资源,特别是在数据库查询中。任务数量受读表、Join、Group By、Limit和数据插入等因素影响。例如,Join的task数由Hash Shuffle决定,limit操作会预估并裁剪task,而数据插入时,分桶表的task数等于分桶数。此外,可通过调整AutoMerge和reduce任务参数来优化Task数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

因为任务的分配是以Task为粒度执行的,每一个Task同时只会执行在一个Executor上,是用一个vCore资源,因此如果要充分利用上CPU,就需要干预Task数量。

以如下SQL为例:

select 

       l_orderkey, 

       sum(l_extendedprice * (1 - l_discount)) as revenue, 

       o_orderdate, 

       o_shippriority 

from 

       customer, orders, lineitem 

where 

       c_mktsegment = 'BUILDING' 

       and c_custkey = o_custkey 

       and l_orderkey = o_orderkey 

       and o_orderdate < date '1995-03-15' 

       and l_shipdate > date '1995-03-15' 

       and o_orderdate > date '1994-11-13' 

       and l_commitdate < date '1995-06-14' 

group by 

       l_orderkey, 

       o_orderdate, 

       o_shippriority 

order by 

       revenue desc, 

o_orderdate 

limit 10;

DAG图

image.png

stage的task情况

image.png

任务一共分成7个stage(此处以星环WR计算引擎情况为例)
stage1: table read (customer) [104 tasks]
stage2: table read (orders) [605 tasks]
stage3: table read (lineitem) [1000 tasks]
stage4: 1&2 join → tmp1 [605 tasks]
stage5: tmp1 & 3 join → tmp2 [1000 task]
stage6: tmp2 group by order by [600 task]
stage0: limit & sink result [1 task]

大家有没有考虑过是什么影响了task数量呢

读表:
Holodesk表,读表的task数量,仅和经过元数据过滤后要读取的block数量有关。
如分区过滤,分桶过滤,数据文件minmax Filter过滤等,会在读表时进行裁剪
但是task数本身和分桶数量并没有直接的关系

image.png

image.png

1. Join & group by:

因为是以Shuffle来切分Stage的,因此只有两个算子之间需要进行数据的重分布(使用Hash Shuffle)时,会需要重新计算下一个Stage的task数,而计算的方式为:
group by 的task为:上一个stage task数 * hive.groupby.aggregateratio(default:0.6)
join 的task为:一般是task更多的stage task数 * hive.join.aggregateratio(default:1.0)
因此可以看到,示例的任务中,stage4的task数位605,stage5的task数是1000,而stage6的task数就是600了

2. limit:

limit时会进行预估,裁剪掉不必要的task。示例中就是裁剪后只剩1个task

默认会先2个Task执行(参数 ngmr.num.parts.try.limit 决定,limit不够再起新的task),因此不要进行大数据量的limit,性能非常差

3. insert:

那么如果我们不是select任务,而是向目标表中插入数据呢?
数据插入时,Stage的task数取决于是否向分桶表插入数据。因为分桶的逻辑是根据数据的Hash值进行数据的分布计算,将数据写到不同的Tablet中,因此入库时的task数量一般是:

  1. 分桶表:无论是否分区,入库的task数均为分桶数
  2. 非分桶表:入库的task数位上一个stage的task数

4. 全局参数控制:

读表stage的task数参数:
通过开启AutoMerge,让一个Task读取多个Small Block,来实现减少Task数的效果
holodesk.automerge.enabled开启AutoMerge
holodesk.automerge.mergesize设置AutoMerge的数据合并大小阈值
holodesk.automerge.filesize设置AutoMerge的数据合并数量阈值
其他stage的task数参数:
手动设置以下参数,会影响读表stage以外的stage的task数,可以增大或减少task数
mapred.reduce.tasks 设置reduce 个数
mapred.minreduce.tasks 设置最小reduce个数

### Python for 循环在多CPU环境下的并行处理能力 Python 的 `for` 循环本身并不具备多CPU环境下的并行处理能力。如果需要实现并行计算,必须借助外部模块来完成任务的分配和执行。以下是关于 Python 在多CPU环境下实现并行处理的关键点: #### 多线程与多进程的选择 对于 CPU 密集型任务,由于全局解释器锁(GIL)的存在,Python 的多线程无法充分利用多核 CPU 的性能[^2]。这意味着即使生成多个线程,CPU 仍然一次只处理一个线程,导致性能提升有限。因此,在 CPU 密集型任务中,推荐使用多进程来实现并行计算。 #### 默认使用的 CPU 数量 Python 并未为 `for` 循环提供默认的多CPU支持。要实现多CPU并行处理,可以使用 `multiprocessing` 模块中的 `Pool` 类,并通过 `processes` 参数指定使用的进程数。如果未指定 `processes` 参数,则会根据系统中的 CPU 核心数自动设置进程数[^3]。 以下是一个示例代码,展示如何使用 `multiprocessing.Pool` 实现 `for` 循环的并行化: ```python from multiprocessing import Pool def task(x): return x * x if __name__ == '__main__': data = [1, 2, 3, 4, 5] with Pool() as pool: # 自动检测CPU核心数并创建相应数量的进程 results = pool.map(task, data) # 并行执行task函数 print(results) ``` #### 使用 `concurrent.futures` 实现并行化 除了 `multiprocessing` 模块外,还可以使用 `concurrent.futures` 模块中的 `ProcessPoolExecutor` 来实现多进程并行计算。这种方法提供了更简洁的接口,适合快速实现并行任务[^3]。 以下是一个使用 `ProcessPoolExecutor` 的示例: ```python from concurrent.futures import ProcessPoolExecutor def task(x): return x * x if __name__ == '__main__': data = [1, 2, 3, 4, 5] with ProcessPoolExecutor() as executor: results = list(executor.map(task, data)) # 并行执行task函数 print(results) ``` #### 性能考量 在选择并行机制时,需考虑任务类型和数据共享需求: - 对于 I/O 密集型任务,多线程可能更为合适,因为线程间的通信开销较低。 - 对于 CPU 密集型任务,多进程是更好的选择,尽管其启动和通信开销较高,但能够有效利用多核 CPU 的性能。 --- ###
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值