【问题标题】:Speed up cython loop by copying from a list to a numpy array通过从列表复制到 numpy 数组来加速 cython 循环
【发布时间】:2013-07-28 08:11:39
【问题描述】:

我正在编写一些性能密集型代码,并希望从 cythonistas 那里获得一些关于如何进一步改进它的反馈。我编写的函数的目的有点难以解释,但它们所做的并不是那么令人生畏。第一个(大致)采用两个数字列表字典并将它们连接起来以获得一个数字列表字典。它只运行一次,所以我不太关心优化它。第二个首先调用第一个,然后使用其结果基本上将存储在 numpy 数组中的索引与数组列表中的数字交叉,以在 (pybloomfiltermmap) 布隆过滤器上形成查询(新数字)。

我已经确定这是由于我的嵌套循环和减少了使用的循环数量,将只需要发生一次的所有内容移出循环,并尽我所知键入了所有内容。尽管如此,第二个函数中 i 的每次迭代都需要大约 10 秒,这太多了。我在 html 编译输出中仍然看到黄色的主要内容是由于列表和 numpy 数组中的索引访问,所以我尝试用所有 numpy 数组替换我的列表,但没有得到任何改进。如果您能提供任何反馈,我将不胜感激。

#cython: boundscheck=False
#cython: wraparound=False

import numpy as np
cimport numpy as np

def merge_dicts_of_lists(dict c1, dict c2):
    cdef dict res
    cdef int n, length1, length2, length3
    cdef unsigned int i, j, j_line, jj, k, kk, new_line

    res =  {n: [] for n in range(256)}
    length1 = len(c1)

    for i in range(length1):
        length2 = len(c1[i])
        for j in range(length2):
            j_line = c1[i][j]
            jj = (j_line) % 256
            length3 = len(c2[jj]) 
            for k in range(length3):
                kk = c2[jj][k]
                new_line = (j_line << 10) + kk
    res[i].append(new_line)
    return res


def get_4kmer_set(np.ndarray c1, dict c2, dict c3, bf):
    cdef unsigned int num = 0
    cdef unsigned long long query = 0
    cdef unsigned int i, j, i_row, i_col, j_line
    cdef unsigned int length1, length2
    cdef dict merge 
    cdef list m_i 

    merge = merge_dicts_of_lists(c2, c3)
    length1 = len(c1[:,0])
    for i in range(length1):
        print "i is %d" % i
        i_row = c1[i,0]
        i_col = c1[i,1]
        m_i = merge[i_col]
        length2 = len(m_i)
        for j in range(length2):
            j_line = m_i[j]
            query = (i_row << 24) + (i_col << 20) + j_line
            if query in bf:
                num += 1
    print "%d yes answers from bf" % num

【问题讨论】:

  • 您的代码看起来像一团糟,可以通过使用正确的数据容器进行很多优化:似乎您的 dicts 具有从 0 到 256 的非负整数作为键,这喊出了列表ndarrays 给我。无论如何,如果将内部列表转换为 ndarray,则可以更快地访问底层数据,避免 Python 调用 __getitem__,详情请参阅 here
  • 谢谢詹姆。正如我所提到的,我尝试用数组/ndarrays 替换列表,但看不到任何好处。很可能是我没有正确输入或声明它们,因为我不清楚如何处理所需的不同长度的 ndarray。此外,是否有理由期望列表比 dict 更快?
  • @roro - dict 必须对密钥进行哈希处理才能找到项目的位置。这永远不会像索引到数组那样快,在 C 级别上,数组是作为指针运算实现的,开销极低。
  • 关于数组,这很清楚,但不是索引访问到列表位置 O(n) 吗? python列表是数组吗?
  • 别在意我的最后一条评论,我发现我混淆了使用指针算法进行遍历和访问。谢谢戴夫P

标签: python numpy cython bloom-filter


【解决方案1】:

为了后代,我添加了一个题外话的答案,但我希望它对某人仍然有用。我在上面发布的代码与我决定保留的代码没有太大区别,因为它已经编译为 Ctyhon html 编译输出所看到的短 C 行。

由于最里面的操作是 Bloom 过滤器查询,我发现最有帮助的是通过两种方式加快该步骤。一个是将 pybloomfiltermmap 使用的哈希函数更改为 murmurhash3 的可用 C++ 实现。我发现 pybloomfilter 正在使用 sha,这对于加密哈希函数来说是相对较慢的。第二个提升来自应用本文中发现的技巧:http://www.eecs.harvard.edu/~kirsch/pubs/bbbf/rsa.pdf。基本上,它说您可以通过使用两个散列值的线性组合而不是 BF 的 k 个不同散列来节省大量计算。这两个技巧一起使查询时间提高了一个数量级(约 5 倍)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-02-18
    • 1970-01-01
    • 1970-01-01
    • 2021-08-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多