【问题标题】:Cython: simple function with 2 lists, what is the fastest way?Cython:带有2个列表的简单函数,最快的方法是什么?
【发布时间】:2015-05-02 10:15:44
【问题描述】:

我想做的——将我的纯 Python 代码转换成 Cython。

纯 Python 代码:

def conflicts(list1,list2):
    numIt = 10
    for i in list1:
        for j in list2:
            if i == j and i < numIt:
                return True
    return False
conflicts([1,2,3], [6,9,8])

到目前为止我的 Cython 代码:

cdef char conflicts(int [] list1,int [] list2):
    cdef int numIt = 10
        for i in list1:
            for j in list2:
                if i == j and i < numIt:
                    return True
    return False
conflicts([1,2,3], [6,9,8])

由于我对 Cython 完全陌生(并不是真正的 Python 专业人士),因此我想获得一些关于我的转换的反馈。我在做正确的事吗?为了使功能更快,我还应该做些什么吗?

更新:

有谁知道我如何在函数的标题中为输入(list1,list2)添加类型?我尝试了“int [:]”,它编译时没有错误,但是当我尝试用两个列表调用函数时,我收到消息“TypeError:'list'没有缓冲区接口”。

【问题讨论】:

  • 我对 cython 了解不多,但在常规 Python 中,您可以通过使用 set 并进行交集(或一堆成员资格测试)而不是迭代来改进一些事情在所有成对的项目上找到匹配项。性能将是O(M+N) 而不是O(M*N)
  • 用分析器测试性能怎么样?我假设你以前这样做过,所以你发现confilcts 不够快。因此,只需对 cython 版本执行相同操作即可。如果你没有配置文件,你怎么知道这个功能不够快?也许还有另一个更大的瓶颈需要处理……

标签: python performance optimization cython


【解决方案1】:

可以声明“i”和“j”以优化您的代码。使用 cython 进行的第一次优化是使用显式声明完成的。

你可以使用

cython -a yourcode.py

并查看一些可能的更改的自动建议,以使用 cython 优化您的 python 代码(黄线)。您可以使用生成的 c 模块(工作完美!)。

一些手写 cython 优化:
+ 对 list1 和 list2 使用类型 list
+ bint 类型用于 conflicts 函数,因为它返回布尔值。
+ 获取列表长度,因为 for 循环需要结束索引。
+ 在 int 数组中映射列表(因为列表只有整数值)。

cdef bint conflicts(list list1, list list2):
    cdef int numIt = 10
    cdef int i, j
    cdef int end_index = len(list1)
    cdef int[:] my_list1 = list1
    cdef int[:] my_list2 = list2

    for i in range(end_index):
        for j in range(end_index):
            if my_list1[i] == my_list2[j] and my_list1[i] < numIt:
                return True
    return False
conflicts([1,2,3], [6,9,8])

【讨论】:

  • 你对 i 和 j 完全正确,甚至没有看到:) 我在 ipython 中使用“%%cython -a”命令来查看 cython 生成的代码,我你猜这就是你的意思吧?
  • 当你使用-a选项时,会生成一个带有黄色代码行的html页面,建议在其中放置一些静态定义。如果没有出现黄线,您就可以并准备生成 c 代码了。否则,cython 会尝试猜测那些静态定义,这样你就不需要手动编写这些了。
  • 我还有一些黄线.. 你可能知道我如何在我的第一行中为两个列表添加静态定义:“def conflict(list1,list2)”?我尝试了“int [:] list1,int [:] list2”,但是当我调用该函数时,我收到消息“TypeError:'list'没有缓冲区接口”。或者我是否必须在调用函数之前转换我的两个列表?
  • 只使用 list 作为 list1 和 list2 的类型。此外,您需要说 for 循环中的结束索引(例如使用范围)。更复杂的选项是在您的函数内转换 int 数组中的列表,例如 here
  • 非常感谢您的帮助,根据您的回答,我学到了很多东西 :) 最后一个小问题:该函数给了我结果,但也给了我一个异常:“Exception TypeError:”'list' '_cython_magic_73f2048d05377bb2f90492c8fe7aad50.conflicts_cy2' 中没有缓冲区接口”我可以/应该做些什么?
【解决方案2】:

正如我评论的那样,您应该能够通过更改您的算法获得相当大的改进,而完全不会弄乱 cython。您当前的代码是 O(len(list1)*len(list2)),但您可以使用 set 将其缩减为 O(len(list1)+len(list2))。您还可以使用内置的any 函数来简化代码:

def conflicts(list1,list2):
    numIt = 10
    s1 = set(list1)
    return any(x in s1 and x < numIt for x in list2)

根据您希望小于 10 的每个列表中有多少个数字,您可以尝试移动 x &lt; numIt 测试以查看最快的测试(过滤 list1,然后将其转换为 set ,例如,或者将if x &lt; numIt 放在for 之后的any 内的生成器表达式中。

【讨论】:

  • 感谢这个答案!该解决方案的问题在于,我的纯 python 版本已经更快了。 (至少根据 ipython 中的 %timeit )。我什至在没有检查 numIT 和不同大小的输入列表的情况下尝试了它。我想 cython 是获得最快结果的最佳选择
猜你喜欢
  • 2011-01-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-24
  • 1970-01-01
  • 1970-01-01
  • 2022-08-13
  • 1970-01-01
相关资源
最近更新 更多