为何在dplyr::mutate()中furrr::future_map_int()比purrr::map_int()慢?
问题原因
该问题的核心原因是并行开销超过了并行计算带来的收益,具体可拆解为以下几点:
- 任务计算量过轻:调用的
length()是R内置的极轻量函数,单次运算耗时仅为纳秒级,300万次运算总耗时仅6秒,此时furrr并行所需的额外开销完全抵消了多进程计算节省的时间。 - 多进程通信与数据复制开销:使用的
multisession模式需要启动独立的R子进程,运行前要把完整的数据集复制给每个worker进程,运行后还要把各个worker的计算结果汇总回收,300万行的列表列数据复制本身就要消耗数秒时间,进一步拉低了效率。 - 并行收益上限低:仅开启2个worker,理论最大加速比仅为2倍,只要并行开销超过3秒,总耗时就会超过单线程的purrr。
优化方案
- 优先使用向量化操作替代遍历:对于计算列表元素长度的需求,不需要使用purrr或者furrr遍历,直接调用base R的
lengths()函数即可,代码示例如下:
my_data %>% mutate(length_col_a = lengths(col_a))
该函数为完全向量化实现,300万行数据的运算耗时通常不到0.5秒,比purrr快10倍以上,是当前场景的最优解决方案。
2. 仅在满足以下条件时考虑使用furrr并行:
- 单个元素的运算耗时至少在毫秒级以上,总计算量足够大
- 调整furrr的分块参数,减少进程间通信次数:可通过
furrr_options设置合适的分块大小,把数据拆成大块传给worker,避免逐元素传输 - Linux/macOS系统下可换成
plan(multicore)模式,利用写时复制机制避免全量复制数据集,能大幅降低并行开销
内容的提问来源于stack exchange,提问作者Emman
相关产品推荐
相关产品推荐

