最佳实践
目录
最佳实践¶
Dask 数组入门很容易,但要用好它们需要一些经验。本页包含最佳实践建议,并提供了常见问题的解决方案。
使用 NumPy¶
如果您的数据能轻松放入 RAM 且性能不是瓶颈,那么使用 NumPy 可能是正确的选择。Dask 会增加另一层复杂性,这可能会带来麻烦。
如果您只是寻求加速而非可伸缩性,那么可以考虑像 Numba 这样的项目。
选择合适的块大小¶
Dask Array 用户遇到的一个常见性能问题是他们选择了过小的块大小(导致大量开销)或与数据对齐不佳的块大小(导致读取效率低下)。
虽然最优的尺寸和形状高度依赖于具体问题,但块大小低于 100 MB 的情况很少见。如果您处理的是 float64 数据,那么对于一个 2D 数组,这个大小大约是 (4000, 4000)
;对于一个 3D 数组,大约是 (100, 400, 400)
。
您希望选择一个足够大的块大小来减少 Dask 需要考虑的块数量(这会影响开销),但也要足够小,以便许多块可以同时放入内存。Dask 通常会在内存中保留两倍于活动线程数的块。
调整块方向¶
读取数据时,应使块与存储格式对齐。大多数数组存储格式本身就以块的形式存储数据。如果您的 Dask 数组块不是这些存储块形状的倍数,那么您将不得不重复读取相同的数据,这可能会非常耗时。但请注意,存储格式选择的块大小通常比 Dask 理想的大小要小得多,更接近 1MB 而不是 100MB。在这种情况下,您应该选择一个与存储块大小对齐且每个 Dask 块维度都是存储块维度的倍数的 Dask 块大小。
例如,如果我们有一个块大小为 (128, 64)
的 HDF 文件,我们可能会选择 (1280, 6400)
的块形状。
>>> import h5py
>>> storage = h5py.File('myfile.hdf5')['x']
>>> storage.chunks
(128, 64)
>>> import dask.array as da
>>> x = da.from_array(storage, chunks=(1280, 6400))
请注意,如果您提供 chunks='auto'
,Dask Array 将查找 .chunks
属性,并使用它来提供良好的分块。
避免过度订阅线程¶
提示
使用 distributed
调度器时,在使用 Nanny 工作节点时,OMP_NUM_THREADS
、MKL_NUM_THREADS
和 OPENBLAS_NUM_THREADS
环境变量会自动设置为 1
。这有助于在常见情况下避免过度订阅线程。
默认情况下,Dask 将运行与逻辑核心数量相同的并发任务。它假定每个任务大约消耗一个核心。然而,许多数组计算库本身就是多线程的,这可能会导致竞争和低性能。特别是支持大多数 NumPy 线性代数例程的 BLAS/LAPACK 库通常是多线程的,需要明确告知它们只使用一个线程。您可以使用以下环境变量来实现这一点(下面使用 bash export
命令,但这可能会因您的操作系统而异)。
export OMP_NUM_THREADS=1
export MKL_NUM_THREADS=1
export OPENBLAS_NUM_THREADS=1
您需要在启动 Python 进程之前运行此命令,使其生效。
考虑使用 Xarray¶
软件包 Xarray 封装了 Dask Array,因此提供了相同的可伸缩性,同时在处理复杂数据集时也增加了便利性。特别是 Xarray 可以在以下方面提供帮助:
将多个数组作为一个一致的数据集进行管理
一次性读取一组 HDF 或 NetCDF 文件
使用一致的 API 在 Dask Array 和 NumPy 之间切换
Xarray 被广泛应用于各种领域,包括物理学、天文学、地球科学、显微镜学、生物信息学、工程学、金融学和深度学习。Xarray 还有一个活跃的用户社区,善于提供支持。
构建自己的操作¶
通常我们希望执行 Dask Array 中没有完全对应函数的计算。在这种情况下,我们可以使用一些更通用的函数来构建自己的操作。这些函数包括:
|
张量操作:广义内积和外积 |
|
将函数映射到 dask 数组的所有块上。 |
|
将函数映射到具有重叠区域的数组块上 |
|
通用的归约操作 |
这些函数可以帮助您将为 NumPy 函数编写的函数应用于更大的 Dask 数组。