【问题标题】:Numba decreases performance on simple for-loop functionNumba 降低了简单 for 循环函数的性能
【发布时间】:2020-07-11 15:26:53
【问题描述】:

我正在处理相对较大的数据集(超过 100,000 个元素),需要为这些数据集创建邻接矩阵。

我已经编写了一个非常基本的 for 循环,可以为给定的连接节点 (nx2) 完成此操作

nodes = np.random.randint(20000, size=(20000, 2))

def adjMat(node_list):
    n = np.max(node_list)
    A = np.zeros((n, n))
    for tail, head in node_list:
        A[tail-1, head-1] = 1
    return A

这很好用,并没有我想象的那么慢,但假设我可以通过使用 numba 来实现这个超级简单的功能,从而显着提高性能。

所以我添加了两个 jitted 函数(一个使用并行)来查看性能差异。我还刚刚包含了 networkx,看看它是否得到了很好的优化。

@njit()
def adjMat_numba(node_list):
    n = np.max(node_list)
    A = np.zeros((n, n))
    for tail, head in node_list:
        A[tail-1, head-1] = 1
    return A

@njit(parallel = True)
def adjMat_numba_para(node_list):
    n = np.max(node_list)
    A = np.zeros((n, n))
    for tail, head in node_list:
        A[tail-1, head-1] = 1
    return A

def getAdjacenyList(node_list):
    G = nx.Graph([e for e in node_list])
    A = nx.convert.to_dict_of_lists(G)

    return A

这是我在 20000 对连接节点上测试的输出:

%timeit a = adjMat(nodes)
112 ms ± 3.7 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%timeit b = adjMat_numba(nodes)
1.34 s ± 41.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit c = adjMat_numba_para(nodes)
251 ms ± 3.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit d = getAdjacenyList(nodes)
149 ms ± 3.31 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

令我惊讶的是,使用 numba 实际上会使函数慢几倍,而且即使在并行模式下,它仍然不如 for 循环快。 Numba 似乎也比 for 循环使用更多的内存。另外,我很惊讶 networkx 比 for 循环慢 - 我原以为唯一目的是处理这类问题的库会更快。

我的 numba 装饰器有什么问题吗?有没有更好的选择来快速有效地创建邻接矩阵?

我正在使用 pycharm 在 12 核 linux 桌面上运行这些测试。

【问题讨论】:

  • numba 需要时间来初始化和编译,运行这些函数两次并检查每个函数的第二次运行时间。如果它只运行一次,那么没有它可能会更好。

标签: python numpy numba adjacency-matrix


【解决方案1】:
In [22]: node_list = np.array([[0,1],[1,4],[4,2],[2,0]])                                                                                                                                     

In [27]: adjMat(node_list+1)                                                                         
Out[27]: 
array([[0., 1., 0., 0., 0.],
       [0., 0., 0., 0., 1.],
       [1., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0.],
       [0., 0., 1., 0., 0.]])

一种非迭代的 numpy 方法:

In [28]: res = np.zeros((5,5))                                                                       
In [29]: res[node_list[0],node_list[1]] = 1                                                          
In [30]: res                                                                                         
Out[30]: 
array([[0., 1., 0., 0., 0.],
       [0., 0., 0., 0., 1.],
       [0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0.]])

这真的需要numba吗?

【讨论】:

  • 好吧,我假设 Numba 会显着加快计算速度,尤其是在要处理 500K 对时。这不是 Numba 的目的吗 - 优化标准 python。诚然,您建议只使用 numpy 是我的大脑想要提出的,但未能提出。比 for 循环快几倍。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-14
  • 2018-01-20
  • 2014-10-27
  • 1970-01-01
  • 2012-04-18
相关资源
最近更新 更多