[Feat][Experimental] Support parallel computing (embarassingly parallel)
本PR主要为PaddleX增加一个实验性质的并行加速功能,旨在优化代码库中广泛存在的类似:[func(item) for item in data]这样的过易并行的逻辑。
PR内容
- 新增基于
joblib的并行加速功能。- 新增
maybe_parallelize函数,可用于标记代码库中的过易并行逻辑,并在配置满足要求时将其并行化。 - 支持设置一个全局执行器以复用线程池/进程池,提升效率;也支持通过
joblib.parallel_config从库的外部定制PaddleX的默认并行计算行为。 - 通过环境变量控制该功能是否开启,默认关闭。
- 新增
- 基于新增的并行加速功能,优化了数据读取操作、大部分模块的前后处理操作以及部分产线的操作。目前的实现,在我的机器上,batch size为32时,启动并行计算功能后,默认配置可以将PP-DocLayout-L的端到端推理时间缩短一半,将PP-OCRv4_mobile_det的预处理时间缩短为原本的1/10。
- 定位到目前OCR类产线推理的一大性能瓶颈在于
scipy.ndimage.rotate的速度太慢了。使用OpenCV编写了替代的高效实现,初步测量对1024*2048的大图加速可以达到近百倍,但与原实现不是完全对齐(看起来可能主要是输出尺寸和align-corner方面有差别),需确认这个替代实现是否可接受。在我的机器上实验,使用新的实现,可以让OCR产线处理一个6页示例PDF的时间从13s降低到6s。 => 2025.3.26,替代实现的精度被相关同学评估为可以接受,为了让这个PR的内容更专一,这一点涉及的修改被移动到更相关的 #3714 。
缺陷与待办
- 新增的并行加速功能对OCR类产线的优化不明显,这主要是因为当前OCR类产线的许多模块接受的batch size都是1。
- 需要对改造的前后处理等操作进行正确性验证和benchmark,评估是否适合并行化。目前只对OCR类模块做了评估。
Thanks for your contribution!
对于并行这个问题。我最近在考虑是否有必要,在基类支持非generator行为的predict方法(假定为get_prediction()),即对于输入数据一次性处理并返回结果,和这个PR类似都是解决该问题。
结合这个PR的思路,我觉得或许可以在基类定义get_prediction(),其实现同样沿用这个PR的实现逻辑,这样的好处是,相比maybe_parallel,get_prediction更加直观,对用户可能更友好。
Bobholamovic也评估下呢。
对于并行这个问题。我最近在考虑是否有必要,在基类支持非generator行为的predict方法(假定为
get_prediction()),即对于输入数据一次性处理并返回结果,和这个PR类似都是解决该问题。 结合这个PR的思路,我觉得或许可以在基类定义get_prediction(),其实现同样沿用这个PR的实现逻辑,这样的好处是,相比maybe_parallel,get_prediction更加直观,对用户可能更友好。 Bobholamovic也评估下呢。
maybe_parallel主要是作为内部API,初衷是用来标记可以执行优化的逻辑,专注于固定的pattern,试图相对降低开发者(与用户相对)编写并行代码的难度。实际上目前这个方案的一个很大的问题是允许在什么粒度施加maybe_parallel——要是之后代码变得复杂,无意中出现了嵌套parallel的情况,就有可能会出现死锁等意料外的问题。也是出于这个原因,现在我选择只在一些相对“标准化”的位置(比如按照开发文档,每个模块都有标准化的布局)添加,其他的多数位置有些太“自由”了,可能只能逐个文件来分析。不过,如果我们进一步扩大“标准化”的范围,比如基类能有get_prediction这样的契约,我想应该对降低开发成本、减小出错概率有很大帮助,直观的好处就是我们可以在一个统一的粒度做统一的控制。目前唯一可能担心的点是如果这个固定的粒度比较粗,有可能加速效果不如细粒度的优化。当然,这些都需要测试后才知道,整体上我支持这个想法~