最佳实践
内容
最佳实践¶
开始使用 Dask 数组很容易,但要*熟练*使用它们确实需要一些经验。本页包含最佳实践的建议,并包括常见问题的解决方案。
使用 NumPy¶
如果你的数据可以轻松地放入内存,并且你不受性能限制,那么使用 NumPy 可能是一个正确的选择。Dask 增加了另一层复杂性,这可能会成为障碍。
如果你只是寻找速度提升而不是可扩展性,那么你可能需要考虑像 Numba 这样的项目。
选择一个合适的块大小¶
Dask Array 用户常见的一个性能问题是他们选择的块大小要么太小(导致大量开销),要么与数据对齐不佳(导致读取效率低下)。
虽然最佳的大小和形状高度依赖于具体问题,但很少见到低于100 MB的块大小。如果你处理的是float64数据,那么对于一个二维数组来说,这大约是``(4000, 4000)``的大小,而对于一个三维数组来说,这大约是``(100, 400, 400)``的大小。
您希望选择一个较大的块大小,以减少Dask需要考虑的块数(这会影响开销),但也要足够小,以便许多块可以同时放入内存中。Dask通常会在内存中保留的块数是活动线程数的两倍。
调整你的块¶
在读取数据时,您应该将数据块与存储格式对齐。大多数数组存储格式本身以数据块的形式存储数据。如果您的 Dask 数组数据块不是这些数据块形状的倍数,那么您将不得不重复读取相同的数据,这可能会很昂贵。但请注意,通常存储格式选择的数据块大小远小于 Dask 理想的大小,更接近 1MB 而不是 100MB。在这些情况下,您应该选择一个与存储数据块大小对齐的 Dask 数据块大小,并且每个 Dask 数据块维度都是存储数据块维度的倍数。
例如,如果我们有一个HDF文件,其块大小为 (128, 64)
,我们可能会选择一个块形状为 (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 数组将查找 .chunks
属性并使用它来提供良好的分块。
避免过度订阅线程¶
小技巧
在使用 distributed
调度器时,当使用 保姆 工作线程时,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 文件中读取
在 Dask 数组和 NumPy 之间切换,使用一致的 API
Xarray 被广泛应用于多个领域,包括物理学、天文学、地球科学、显微镜学、生物信息学、工程学、金融学和深度学习。Xarray 还拥有一个活跃的用户社区,擅长提供支持。
构建你自己的操作¶
通常我们希望执行在 Dask Array 中没有精确函数的计算。在这些情况下,我们可能能够使用一些更通用的函数来构建我们自己的函数。这些包括:
|
张量操作:广义内积和外积 |
|
将一个函数映射到 dask 数组的所有块上。 |
|
在具有一定重叠的数组块上应用函数 |
|
简化的一般版本 |
这些功能可以帮助你将你为NumPy函数编写的函数应用于更大的Dask数组。