为什么Dask中的点积比NumPy慢?

4
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

有人知道可能的原因吗?这里有什么我漏掉了吗?

这个问题基本上是由矩阵大小引起的,而不是点积运算,我相信块在其中扮演了重要角色。 - saopayne
@saopayne,谢谢你。这些代码块确实起着重要的作用。 - istern
1
调整和解决问题正是我们报名参加的原因 :) - saopayne
2个回答

5

调整块大小

@isternberg提供的答案是正确的,您应该调整块大小。好的块大小选择应遵循以下规则

  1. 块大小应小到可以轻松适应内存。
  2. 计算该块时所需的时间需要明显多于每个任务1毫秒的dask开销(因此100毫秒-1秒是一个不错的数值)。
  3. 块应与您要执行的计算相对应。例如,如果您计划频繁沿某个维度分片,则如果块对齐,则需要触摸的块更少,则更有效率。

我通常选择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

0
在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 中 chunks 的更多信息,请参见docs

编辑:正如 @MRocklin 所写,要真正获得计算时间,必须在函数上调用 .compute()


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接