对于为 Dask 数组选择一个好的块大小感到困惑吗?
数组块不能太大(会内存不足),也不能太小(Dask 引入的开销会变得难以承受)。那么我们如何才能做到正确呢?
这是一个两步过程
Dask 数组是大型结构,由许多小块组成。通常,每个小块都是一个独立的 numpy 数组,它们被组合在一起以形成一个大得多的 Dask 数组。
您可以在此文档页面上找到有关 Dask 数组块的更多信息:https://docs.dask.org.cn/en/latest/array-chunks.html
如果您有一个 Dask 数组,您可以使用 chunksize 或 chunks 属性来查看有关块的信息。您还可以使用 Dask 数组的 HTML 表示形式来可视化此信息。
arr.chunksize 显示最大的块大小。对于预期块大小大致均匀的数组,这是一个总结块大小信息的好方法。
arr.chunks 显示 Dask 数组中所有维度上所有块的完整显式大小(参见此处的项目 3)。这更详细,是处理非规则块数组时的不错选择。
如果数组块太小,效率会很低。这是为什么?
使用 Dask 会为计算中的每个任务引入一定量的开销。这种开销是 Dask 最佳实践建议您避免过大图的原因。这是因为如果每个任务完成的实际工作量非常少,那么开销时间与有用工作时间的百分比就不理想。
通常,Dask 调度器协调单个任务需要 1 毫秒。这意味着我们希望每个任务的计算时间相对较长,例如:秒而不是毫秒。
这可能很难凭直觉理解,所以这里有一个类比。假设我们在建造一栋房子。这是一项相当大的工程,如果只有一个工人,建造时间会太长。所以我们有一个工人团队和一个工地工头。工地工头相当于 Dask 调度器:他们的工作是告诉工人需要做什么任务。
假设我们有一大堆砖块要砌墙,放在建筑工地的角落里。如果工头(Dask 调度器)告诉工人一次去取一块砖,然后把每一块都带到正在砌墙的地方,你可以看出这会非常慢且效率低下!工人们大部分时间都花在墙和砖堆之间来回移动上。花在实际砌砖工作上的时间要少得多。
相反,我们可以用更智能的方式来做。工头(Dask 调度器)可以告诉工人们每次去搬回一整独轮车砖块。现在工人们花在墙和砖堆之间来回移动上的时间大大减少了,墙也会砌得快得多。
如果 Dask 数组块太大,这也是不好的。为什么?块太大是不好的,因为这样很可能会耗尽工作内存。您可能会看到内存不足错误发生,或者看到性能显著下降,因为数据会溢出到磁盘。
当在太少的工作进程上加载了太多数据到内存时,Dask 会尝试将数据溢出到磁盘而不是崩溃。将数据溢出到磁盘会使事情运行得非常慢,因为所有这些额外的磁盘读写操作。事情不仅会变慢一点点,而是会慢很多,所以注意这一点是明智的。
为了注意这一点,请查看 Dask 仪表盘上的 工作进程内存图。橙色条表示您已接近内存限制,而灰色表示数据正在溢出到磁盘 - 不妙!有关更多提示,请参阅下面关于使用 Dask 仪表盘的部分。
如果您从磁盘读取数据,存储结构将决定您的 Dask 数组块的形状。为了获得最佳性能,请选择与数据存储方式良好对齐的块。
来自 Dask 最佳实践中关于如何定位块的内容
读取数据时,应将块与存储格式对齐。大多数数组存储格式本身就将数据存储在块中。如果您的 Dask 数组块不是这些块形状的倍数,那么您将不得不重复读取相同的数据,这可能会很昂贵。但请注意,存储格式选择的块大小通常比 Dask 理想的大小要小得多,更接近 1MB 而不是 100MB。在这种情况下,您应选择一个与存储块大小对齐且每个 Dask 块维度是存储块维度的倍数的 Dask 块大小。
磁盘上的一些数据存储结构的示例包括
选择合适块大小的第二部分是监控 Dask 仪表盘,看看是否需要进行任何调整。
如果您对 Dask 仪表盘不太熟悉,或者有时忘记在哪里找到某些仪表盘图(例如工作进程内存图),那么您可能会喜欢这些快速视频教程
我们建议在使用 Dask 时始终打开仪表盘。这是了解哪些运行良好或不佳的绝佳方式,以便您可以进行调整。
需要注意的不良迹象包括
这是一个良好计算期间 Dask 仪表盘的示例(此视频中的时间 6:12)。
作为对比,这是一个不良计算期间 Dask 仪表盘的示例(此视频中的时间 6:57)。
在此示例中,效率低下是因为块太小,所以在任务流图中看到大量空白区域和红色工作进程通信。
如果在计算过程中需要更改 Dask 数组的分块,可以使用 rechunk 方法。
警告:重新分块 Dask 数组是有代价的。
由于这些原因,最好选择一个好的初始块大小并避免重新分块。
然而,有时存储在磁盘上的数据对齐不佳,可能需要重新分块。例如,这里是 Draga Doncila Pop 谈论卫星图像数据块对齐的内容。
rechunker 库在这些情况下可能很有用
Rechunker 接受存储在持久存储设备(例如文件系统或云存储桶)中的输入数组(或数组组),并将具有相同数据但不同分块方案的数组(或数组组)写入新位置。Rechunker 设计用于在并行执行框架(例如 Dask)中使用。
最后,请记住,您不仅需要考虑内存中数组块的大小,还需要考虑分析函数消耗的工作内存。有时在 Dask 中这被称为“非托管内存”。
“非托管内存是指 Dask 调度器不直接知道的 RAM,它可能导致工作进程内存不足,并导致计算挂起和崩溃。”——Guido Imperiale
以下是处理非托管内存的一些技巧
我们希望这篇博客文章能帮助您弄清楚如何在 Dask 中选择好的块大小。这篇博客文章的灵感来自于这条 Twitter 推文。如果您想在 Twitter 上关注 Dask,可以访问 https://twitter.com/dask_dev