使用Dask库@delayed装饰器为何性能反而不及串行代码?
Dask @delayed性能反直觉问题解惑
问题背景
我正在学习Dask库的使用,尝试用@delayed装饰器实现并行计算,结果发现并行代码的运行时长反而远高于普通串行代码。即使增加迭代次数,结果依然一致,特此寻求解惑。
测试环境:Windows系统、4物理核8逻辑核、Python 3.11.0、Dask 2023.6.0
带Dask delayed装饰器的代码
import time import dask from dask import delayed @delayed def square(x): return x*x @delayed def double(x): return x*2 @delayed def add(x, y): return x + y @delayed def sum(output): sum = 0 for i in output: sum += i return sum t1 = time.time() # 循环调用上述函数处理每个数据 output = [] for i in range(99999): a = square(i) b = double(i) c = add(a, b) output.append(c) total = dask.delayed(sum)(output) print(total) print("Elapsed time: ", time.time() - t1)
运行时长:~8.46s
普通串行代码
import time def square(x): return x*x def double(x): return x*2 def add(x, y): return x + y def sum(output): sum = 0 for i in output: sum += i return sum t1 = time.time() # 循环调用上述函数处理每个数据 output = [] for i in range(99999): a = square(i) b = double(i) c = add(a, b) output.append(c) total = sum(output) print(total) print("Elapsed time: ", time.time() - t1)
运行时长:~0.043s
原因分析与解决建议
核心原因
- 任务粒度极小,调度开销远超计算成本:你定义的
square、double、add都是极简单的算术运算,执行耗时可以忽略,但Dask为每个任务创建延迟对象、构建依赖关系的开销远大于计算本身。串行代码直接执行运算,无额外开销,自然更快。 - 未实际触发并行计算:你的Dask代码只完成了任务图的构建,没有调用
total.compute()触发实际的并行执行。这段代码的耗时几乎全在构建近30万个任务的依赖图上,完全没用到Dask的并行能力。 - 场景不匹配:Dask的优势在于处理计算密集、任务粒度大的场景(如大数据处理、复杂模型训练),或处理超出内存的数据集,而非这种简单循环的轻量运算。
解决建议
- 合并细粒度任务:把循环内的多个轻量运算打包成一个延迟函数,大幅减少任务数量。例如将
square、double、add合并为一个process_single函数,再用@delayed装饰。 - 调用
compute()触发执行:构建完任务图后,必须调用.compute()才能让Dask调度并行计算。 - 改用Dask Collections:如果是处理数组或表格数据,优先使用Dask Array、Dask DataFrame,它们会自动优化任务粒度和调度逻辑。
修改后的示例代码
import time import dask from dask import delayed @delayed def process_single(i): a = i*i b = i*2 return a + b @delayed def sum_batch(output): return sum(output) t1 = time.time() output = [process_single(i) for i in range(99999)] total = sum_batch(output).compute() # 触发并行计算 print(total) print("Elapsed time: ", time.time() - t1)
内容的提问来源于stack exchange,提问作者madmatrix
相关产品推荐
相关产品推荐

