三个月前,我信心满满的写下了dask框架的各种操作技巧,短短三个月不到,在使用了dask的各种功能后,心中顿感五味杂陈,在真正被dask的坑打脸后,我才发现,相比成熟可靠的分布式框架,dask还差的很远。
我承认可能是我太过苛求了,其中也不乏并非dask的真正问题,但还是不得不吐槽下,希望对自己以后的框架设计也有借鉴和警醒的作用。
以下就是我的吐槽清单,哈哈:
1.
dask集群环境不一致问题
会导致dask集群既能生成一部分文件,又会报错,最好采取集群中的机器调试逐步加入
2.
dask-cuda 集群崩溃重启
需要清空 local_directory ,否则集群会重新加载之前的工作状态,导致集群内存无法释放
3.
dask集群执行任务时,再增加worker会出现错误:
dask worker KeyError: xxx_func_name-xxx_task_key
example: KeyError: 'process_one_file-1a131da038cb807b28f5ba019463f68c'
停止从机上的worker,不同的dask集群worker设置不同的 local-directory
4.
更新package后,不仅仅只是重启dask worker,还需要先删除worker目录
5.
不要使用dask多进程写单个文件,会造成文件写冲突,部分数据丢失,但系统不会报错
6.
在一个 dask-worker 命令里启动worker比多条 dask-worker 命令启动占用系统内存要少
7.
执行程序,发现任务很快完成,且不走dask中的方法时,应该是连接的cluster地址有问题
8.
出现错误 distributed.comm.core.CommClosedError concurrent.futures._base.CancelledError
dask升级到2.25 未能解决问题,从git上查阅 https://github.com/dask/distributed/pull/2477 2019年就出现的问题
将rapidsai整体升级到0.15后,运行未出现问题,训练后预测再次出现该问题,删除local_directory预测,问题未出现
再次预测,dask在完成1个loop后,会出现进程假死的现象
堆栈中出现大量等待的线程:AsyncProcess Dask Worker process (from Nanny) watch message queue
初步推测是某些cuda设备未能正常返回导致
减少dask中cuda设备的数量再次运行,出现:OSError: Timed out trying to connect to 'tcp://127.0.0.1:37321' after 10 s
删除local_directory预测,依然会出现假死(一定概率)
9.
出现错误 ERROR - Workers don't have promised key
在 LocalCUDACluster 中添加参数 interface='lo'
10.
dask在调用了client.map后的代码不会做语法检查(想不到吧,嘿嘿)
11.
dask的worker不会进行垃圾回收管理,进入worker中的方法需要自己维护变量
注意:调用 del xxx 或 gc.collect() 均无效
目前仅能进行小规模计算(<3000)或定时重启
12.
dask的2.25.0版(2.24.0没问题)不再能输出logging包装的日志,print可以输出
13.
dask报错 TypeError: can't pickle _asyncio.Task objects
dask的task中不能嵌套task,并且不能嵌套与多进程有关的任何对象,否则都会报错
即dask的task中不能再使用client.map、multiprocessing.pool、swifter
14.
dask的2.24.0 2.25.0长时间运行大批量任务均会出现OOM
直接导致worker崩溃或者dask假死(使用gc.collect()无效)
并发现scheducler的日志莫名消失,初步判断可能是在dask崩溃时,向日志里写入了超出可用内存的内容导致
15.
多个dask集群如果不指定端口,会启动失败,无任何提示
或者使用ucx协议时出现错误:UCX ERROR send(fd=-1) failed: Bad file descriptor
(虽然dask默认会检查端口,发现占用后设置随机端口,但显然并未凑效)
16.
执行大量dask任务时,LocalCluster会出现莫名其妙的client close问题
改用client连接host方式后,能有所缓解(显然LocalCluster的worker管理也有问题)
17.
resource_tracker.py:216: UserWarning: resource_tracker: There appear to be 144 leaked semaphore objects to clean up at shutdown
warnings.warn('resource_tracker: There appear to be %d '
通常意味着dask对象内存占用太多,dask已经崩溃了
18.
dask.map的方法不要返回True/False, 否则会导致dask在gather结果时产生误判,一直阻塞
19.
dask报错:distributed.scheduler - ERROR - Workers don't have promised key
对程序运行暂无影响,设置 interface='lo' 改为 interface='eth2'
20.
dask-cuda报错:RuntimeError: Set changed size during iteration
查询发现是ucx组件的bug,系统若支持ucx会默认使用该协议(且python环境安装了ucx),更换为tcp协议,protocol="tcp"
实际发现,运行一段时间后依然会出现上述错误
21.
dask有个倒霉的设计,当dask dataframe出现在client.map中时,可以使用的 worker 数量配置就被client的配置覆盖了
即使在compute方法中指定worker数量也是无效的,这就导致在map中read_csv出奇的缓慢
22.
dask-cuda中的设置:memory_limit=device_memory_limit=单个GPU显存
若 memory_limit 设置过大,则dask会一直分派新的任务,直到 memory_limit 占满
吐个槽:如果dask会自动调度任务,则该配置没有必要暴露给用户,要么任务分派机制也让用户自行配置
23.
LocalCUDACluster的设计问题:使用全局worker来控制GPU资源的使用,这就导致在执行常规CPU任务时,worker数量不足
24.
出现错误: XGBoostError updater_gpu_hist.cu:723: Exception in gpu_hist: device_helpers.cu: Check failed: n_uniques == world (1 vs. 4) : Multiple processes within communication group running on same CUDA device is not supported
说明GPU上有其他进程占用了资源,检查GPU上未结束的进程kill掉, LocalCUDACluster 与 numba 发生冲突
25.
dask进程阻塞,一直出现warning:Memory use is high but worker has no data to store to disk. Perhaps some other process is leaking memory? Process memory: -- Worker memory limit:
github上提示关闭pandas的warning即可解决该问题:pd.options.mode.chained_assignment = None
(实际发现进程内存会一直增长,耗尽操作系统内存, 控制台的进度条也不可见,手工增加 gc.collect()后,内存增速变缓,发现进程并未执行,改用多进程顺利执行)
26.
严重的bug:xarray 使用 rename_dims 方法后,会丢失原始的 Coords 值,导致float格式的 lat, lon 索引变整数
27.
dask-cuda 命令行工具在执行分布式xgboost训练任务时,不输出任何日志,看似在执行,等待几个小时后,无任何进展 (网络带宽占用过大,导致无法读写新数据)
28.
dask-cuda 一旦出现 Closing dangling stream in
local
=
tcp://172.11.11.111:44188
remote
=
tcp://172.11.11.111:40553
>
意味着 worker 已停止,集群很难再从错误中恢复过来(偶尔能恢复也需要很久),不必再浪费时间等待,重启进程
29.
dask-cuda threads_per_worker 没有效果,增加worker的threads数量,对计算速度并无帮助
30.
conda 不可信任,执行 conda update 会出现 remove 包选项,这些包并不会与当前包发生冲突,删除后导致import error
31.
调用 GPU 的程序不宜执行长时间的跑批任务,否则就会导致进程卡死(可能与numba的GPU内存管理有关)
*** Error in `python': free(): invalid size: 0x00007fe6344c17f0 ***
======= Backtrace: =========
/lib64/libc.so.6(+0x81299)
[
0x7fe7409b8299
]
/home/meteo/anaconda3/envs/rapids/lib/python3.8/site-packages/netCDF4/../netCDF4.libs/libhdf5-849c911b.so.103.1.0(H5MM_xfree+0xe)
[
0x7fe6f77b144e
]
发现pytables的issue中有提到类似问题,降级pytables到特定版本:
pip uninstall tables
conda install pytables (如果安装了rapids,会提示冲突安装失败)
该操作未能解决此bug
……
我相信,实际的bug还会更多,但这次就写到这里了。
我一直认为太多的bug就不能算bug了,而是设计或架构出了问题,看起来dask的架构真的不太妙
虽然现在受制于gpu的使用,只能使用dask-cuda解决
但我也开始刻意去回避dask的使用,能用多进程解决的,就尽量不用dask
如果真要想设计一款好的分布式处理框架,我宁愿相信,更可靠的解决方案应该是设计一个分布式操作系统
毕竟cpu内存等资源的分配和进程的调度,一直都是操作系统该干的事
从语言层面再独立出来一个分布式计算框架,无论是spark还是dask,都给人一种四不像的感觉,并未彻底的解决分布式计算的问题
从代码可移植的角度来看,分布式操作系统也是更好的选择
我又搜刮了下互联网,除了国内一个作者开发的一款Hurricane分布式操作系统与此想法近似外,再也找不到更多的信息
Hurricane网址: https://github.com/samblg/hurricane
看起来谁也不想做吃力不讨好的挖井人啊
希望本人的这堆吐槽能够唤起有识之士对这方面的兴趣,没有兴趣的也可以通过这些吐槽避免掉进坑里,少走些弯路。