在Cython中进行列表/词典的习惯方法?

时间:2009-10-06 23:37:44

标签: c++ python c cython

我的问题:我发现使用原始C ++使用STL映射和向量处理大型数据集通常比使用Cython更快(并且内存占用更少)。

我认为这部分速度惩罚是由于使用Python列表和dicts,并且可能有一些技巧在Cython中使用较少的数据结构。例如,此页面(http://wiki.cython.org/tutorials/numpy)显示了如何通过预定义ND数组的大小和类型在Cython中非常快速地生成numpy数组。

问题:有没有办法与list / dicts做类似的事情,例如:通过大致说明你期望在它们中有多少元素或(键,值)对? 也就是说,是否有一种惯用的方法可以在Cython中将列表/ dicts转换为(快速)数据结构?

如果不是,我想我只需要用C ++编写并包装在Cython导入中。

6 个答案:

答案 0 :(得分:33)

Cython现在具有模板支持,并附带一些STL容器的声明。

请参阅http://docs.cython.org/src/userguide/wrapping_CPlusPlus.html#standard-library

以下是他们给出的例子:

from libcpp.vector cimport vector

cdef vector[int] vect
cdef int i
for i in range(10):
    vect.push_back(i)
for i in range(10):
    print vect[i]

答案 1 :(得分:30)

在Python中执行与在C ++中类似的操作通常会更慢。 listdict实际上实现得很好,但是使用Python对象会产生大量开销,这些对象比C ++对象更抽象,并且在运行时需要更多查找。

顺便提一下,std::vector的实现方式与list非常相似。但是,std::map实际上是以许多操作比dict更慢的方式实现的,因为它的大小变大了。对于每个的适当大的例子,dict克服了比std::map慢的常数因子,并且实际上会更快地执行查找,插入等操作。

如果你想使用std::mapstd::vector,没有什么能阻止你。如果要将它们公开给Python,你必须自己包装它们。如果这种包装消耗了你希望保存的全部或大部分时间,请不要感到震惊。我不知道有任何工具可以让你自动化。

有一些C API调用用于控制具有一些细节的对象的创建。您可以说“创建一个至少包含这么多元素的列表”,但这并不会提高列表创建和填充操作的整体复杂性。当您尝试更改列表时,它肯定不会有太大变化。

我的一般建议是

  • 如果你想要一个固定大小的数组(你谈到指定列表的大小),你可能真的想要一个像numpy数组的东西。

  • 我怀疑您是否希望在代码中使用std::vector而不是list来替换一般替换所需的任何加速。如果你想在幕后使用它,它可能会给你一个令人满意的尺寸和空间改进(我当然不知道没有测量,也不是。)。)。

  • dict实际上做得很好。我绝对不会尝试在基于std::map的Python中引入一个新的通用类型,它在许多重要操作中及时算法复杂度更低,并且至少在某些实现中 - 给用户留下了一些优化那dict已经有了。

    如果我确实想要的东西更像std::map,我可能会使用数据库。这通常是我要做的事情,如果我想要存储在dict(或者我存储在list中的东西)的东西变得太大,我觉得在内存中存储感觉很舒服。 Python在stdlib中有sqlite3,所有其他主要数据库都有驱动程序。

答案 2 :(得分:9)

C ++很快,不仅仅是因为向量的静态声明和进入它的元素,但关键是因为使用模板/泛型,指定向量包含某个元素类型,例如矢量与三元素的元组。 Cython不能做到这一点,听起来很重要 - 它必须在编译时强制执行,不知何故(运行时的类型检查是Python已经做的)。所以现在当你在Cython中从列表中弹出一些东西时,无法事先知道它是什么类型,并且将它放入一个类型变量只会增加一个类型检查,而不是速度。这意味着在这方面没有办法绕过Python解释器,在我看来,这是Cython用于非数字任务的最关键的缺点。

解决这个问题的手动方法是使用cdef类为特定类型的元素或键值组合子类化python list / dict(或者可能是std :: vector)。这与模板生成的代码相同。只要在Cython代码中使用结果类,它就应该提供改进。

使用数据库或数组只解决了一个不同的问题,因为这是关于在容器中放置任意对象(但具有特定类型,最好是cdef类)。

std :: map不应该和dict比较; std :: map按排序顺序维护键,因为它是一个平衡树,dict解决了另一个问题。更好的比较是dict和谷歌的哈希表。

答案 3 :(得分:2)

如果这适用于您的Cython设置,您可以查看Python的标准array模块。我不确定,因为我从未使用过Cython。

答案 4 :(得分:0)

没有办法让原生Python列表/ dicts达到C ++地图/矢量的速度,甚至无法接近。它与分配或类型声明无关,而是支付解释器开销。你提到的例子(numpy)是一个C扩展,用C语言编写正是出于这个原因。

答案 5 :(得分:0)

仅因为此处未提及:您可以轻松地在自定义extension type中包装例如C ++向量。

from libcpp.vector cimport vector

cdef class pyvector:
    """Extension type wrapping a vector"""
    cdef vector[long] _data

    cpdef void push_back(self, long x):
        self._data.push_back(x)

    @property
    def data(self):
        return self._data

通过这种方式,您可以将数据存储在向量中,以实现快速的Cython操作,同时仍然能够从Python端访问数据(有一些开销)。