为什么点积在 dask 中比在 numpy 中慢
why is dot product in dask slower than in numpy
dask 中的点积似乎 运行 比 numpy 慢得多:
import numpy as np
x_np = np.random.normal(10, 0.1, size=(1000,100))
y_np = x_np.transpose()
%timeit x_np.dot(y_np)
# 100 loops, best of 3: 7.17 ms per loop
import dask.array as da
x_dask = da.random.normal(10, 0.1, size=(1000,100), chunks=(5,5))
y_dask = x_dask.transpose()
%timeit x_dask.dot(y_dask)
# 1 loops, best of 3: 6.56 s per loop
有人知道这可能是什么原因吗?我在这里遗漏了什么吗?
调整块时,dask 中的点积计算运行得更快:
import dask.array as da
x_dask = da.random.normal(10, 0.1, size=(1000,100), chunks=1000)
y_dask = x_dask.transpose()
%timeit x_dask.dot(y_dask)
# 1000 loops, best of 3: 330 µs per loop
更多关于 dask 中的区块的信息 docs。
编辑:
正如@MRocklin 所写,要真正获得计算时间,必须在函数上调用 .compute()
。
调整区块大小
@isternberg 的回答是正确的,您应该调整块大小。块大小的良好选择遵循以下规则
- 一个块应该足够小以适应内存。
- 块必须足够大,以便在该块上进行计算所花费的开销远远超过 dask 产生的每个任务的 1ms 开销(因此 100ms-1s 是一个不错的数字)。
- 块应与您要执行的计算保持一致。例如,如果您计划频繁地沿着特定维度进行切片,那么如果您的块是对齐的,那么您必须接触更少的块会更有效。
我通常拍摄 1-100 兆字节大的块。任何比这更小的东西都没有用,而且通常会创建足够多的任务,以至于调度开销成为我们最大的瓶颈。
对原问题的评论
如果您的数组大小仅为 (1000, 100)
,则没有理由使用 dask.array
。相反,使用 numpy,如果你真的关心使用多核,请确保你的 numpy 库链接到有效的 BLAS 实现,如 MLK 或 OpenBLAS。
如果您使用多线程 BLAS 实现,您实际上可能想要关闭 dask 线程。这两个系统将相互破坏并降低性能。如果是这种情况,那么您可以使用以下命令关闭 dask 线程。
dask.set_options(get=dask.async.get_sync)
要实际计算 dask.array 计算的执行时间,您必须在计算结束时添加一个 .compute()
调用,否则您只是计算创建所需的时间任务图,而不是执行它。
更大的例子
In [1]: import dask.array as da
In [2]: x = da.random.normal(10, 0.1, size=(2000, 100000), chunks=(1000, 1000)) # larger example
In [3]: %time z = x.dot(x.T) # create task graph
CPU times: user 12 ms, sys: 3.57 ms, total: 15.6 ms
Wall time: 15.3 ms
In [4]: %time _ = z.compute() # actually do work
CPU times: user 2min 41s, sys: 841 ms, total: 2min 42s
Wall time: 21 s
dask 中的点积似乎 运行 比 numpy 慢得多:
import numpy as np
x_np = np.random.normal(10, 0.1, size=(1000,100))
y_np = x_np.transpose()
%timeit x_np.dot(y_np)
# 100 loops, best of 3: 7.17 ms per loop
import dask.array as da
x_dask = da.random.normal(10, 0.1, size=(1000,100), chunks=(5,5))
y_dask = x_dask.transpose()
%timeit x_dask.dot(y_dask)
# 1 loops, best of 3: 6.56 s per loop
有人知道这可能是什么原因吗?我在这里遗漏了什么吗?
调整块时,dask 中的点积计算运行得更快:
import dask.array as da
x_dask = da.random.normal(10, 0.1, size=(1000,100), chunks=1000)
y_dask = x_dask.transpose()
%timeit x_dask.dot(y_dask)
# 1000 loops, best of 3: 330 µs per loop
更多关于 dask 中的区块的信息 docs。
编辑:
正如@MRocklin 所写,要真正获得计算时间,必须在函数上调用 .compute()
。
调整区块大小
@isternberg 的回答是正确的,您应该调整块大小。块大小的良好选择遵循以下规则
- 一个块应该足够小以适应内存。
- 块必须足够大,以便在该块上进行计算所花费的开销远远超过 dask 产生的每个任务的 1ms 开销(因此 100ms-1s 是一个不错的数字)。
- 块应与您要执行的计算保持一致。例如,如果您计划频繁地沿着特定维度进行切片,那么如果您的块是对齐的,那么您必须接触更少的块会更有效。
我通常拍摄 1-100 兆字节大的块。任何比这更小的东西都没有用,而且通常会创建足够多的任务,以至于调度开销成为我们最大的瓶颈。
对原问题的评论
如果您的数组大小仅为 (1000, 100)
,则没有理由使用 dask.array
。相反,使用 numpy,如果你真的关心使用多核,请确保你的 numpy 库链接到有效的 BLAS 实现,如 MLK 或 OpenBLAS。
如果您使用多线程 BLAS 实现,您实际上可能想要关闭 dask 线程。这两个系统将相互破坏并降低性能。如果是这种情况,那么您可以使用以下命令关闭 dask 线程。
dask.set_options(get=dask.async.get_sync)
要实际计算 dask.array 计算的执行时间,您必须在计算结束时添加一个 .compute()
调用,否则您只是计算创建所需的时间任务图,而不是执行它。
更大的例子
In [1]: import dask.array as da
In [2]: x = da.random.normal(10, 0.1, size=(2000, 100000), chunks=(1000, 1000)) # larger example
In [3]: %time z = x.dot(x.T) # create task graph
CPU times: user 12 ms, sys: 3.57 ms, total: 15.6 ms
Wall time: 15.3 ms
In [4]: %time _ = z.compute() # actually do work
CPU times: user 2min 41s, sys: 841 ms, total: 2min 42s
Wall time: 21 s