计算阶段
内容
计算阶段¶
本页描述了计算的所有部分,一些常见的导致缓慢的原因,以及如何有效地进行性能分析。这是为遇到较大计算量时出现缓慢情况的高级用户准备的。
图构建¶
对 Dask 集合(数组、数据框、包、延迟)的操作会构建任务图。这些图是包含 Python 函数字典,每当某些函数需要在某些数据块上运行时,都会包含一个条目。当这些字典变得很大(数百万个任务)时,构建它们的开销可能会变得相当大。此外,构建图的代码本身可能效率不高。
幸运的是,这个计算过程都是在您自己的计算机上使用普通的 Python 进行的,因此您可以使用 cProfile 模块或 IPython 魔法如 %prun 或 %snakeviz 来对其进行性能分析,就像分析您计算机上的其他 Python 代码一样。
如果在性能分析时没有发现明显的原因,解决这个问题的一个常见方法是,如果可能的话,通过增加块大小来减小图的大小,或者手动将许多操作批处理到较少的函数中。
图优化¶
在你提交图表执行之前,Dask会检查是否可以稍微清理一下图表。这有助于去除不必要的工作,有时还会替换为更高效的操作。不过,和之前一样,如果你的图表非常大(数百万个任务),那么这可能需要一些时间。
同样如前,这一切都在你本地机器上的 Python 环境中进行。你可以使用 dask.optimize 函数单独对优化进行性能分析。
# x, y = dask.compute(x, y)
x, y = dask.optimize(x, y)
人们很少改变优化。它很少是减速的主要原因。
图序列化¶
当你使用分布式调度器时,图表必须发送给调度器进程,然后从那里发送到工作节点。为了发送这些数据,必须首先将其转换为字节。这个序列化过程有时可能会很昂贵,特别是如果你传递的对象非常复杂或非常大。
最简单的分析方法是使用分布式调度器来分析 persist 调用。这将包括上述优化阶段,以及序列化和部分通信阶段(序列化通常是最大的组成部分)。幸运的是, persist 在调用后立即返回,不等待计算实际完成。
大多数情况下,长时间序列化的原因是将大型对象(如 NumPy 数组或 Pandas 数据框)反复放入图表中。Dask 通常会在注意到这种情况时发出警告。通常最好的解决方案是将数据作为任务读入,而不是直接包含它,预先分散大数据,或将它们包装在 dask.delayed 中。有时序列化是由其他复杂的对象问题引起的。这些问题往往非常特定于库,因此很难为它们提供一般的指导方针。
图通信¶
然后必须将图表传达给调度器。您可以查看仪表板的 /system 标签页,以监控调度器的网络通信。目前没有好的方法来分析这一点。
调度¶
调度器现在接收图,并且必须填充其内部数据结构,以便能够有效地将这些任务调度到各个工作节点。
只有在这些数据结构被填充后,仪表盘才会显示任何活动。在按下 compute/persist 和看到活动之间的所有时间都用于上述阶段。
您可以通过仪表板的 /profile-server 页面来分析调度成本。然而,这对用户来说很少有用,因为除非您愿意深入研究调度代码,否则很难在此采取行动。尽管如此,感兴趣的用户可能会发现调度器内部工作机制的分析很有趣。
如果调度成本很高,那么你能做的最好的事情就是减小图的大小,通常通过增加块大小来实现。
执行¶
最终,你的工作线程会收到一些任务并开始执行。你的代码在一个工作线程上运行,并执行它被指示的任何操作。
Dask 的仪表板是一个很好的工具,用于分析和调查性能,特别是 /status 和 /profile 页面。
加速这一阶段通常取决于你提交任务的作者。如果你使用的是自定义代码,或者是NumPy或Pandas的开发者,那么这可能是你。我们鼓励你考虑使用Cython、Numba或其他常用的加速Python代码的解决方案。