dask想说爱你不容易

三个月前,我信心满满的写下了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
看起来谁也不想做吃力不讨好的挖井人啊
 
希望本人的这堆吐槽能够唤起有识之士对这方面的兴趣,没有兴趣的也可以通过这些吐槽避免掉进坑里,少走些弯路。

你可能感兴趣的:(分布式,java,python,linux,大数据)