39

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

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

问题:有没有办法对列表/字典做类似的事情,例如通过大致说明您希望其中有多少元素或(键,值)对?也就是说,是否有一种惯用的方式将列表/字典转换为 Cython 中的(快速)数据结构?

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

4

6 回答 6

37

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]
于 2011-12-14T17:09:26.567 回答
34

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

顺便说一句,std::vector它的实现方式与list. std::map但是,实际上是以一种方式实现的,即许多操作比dict它的大小变大时要慢。对于每个适当大的示例,dict克服了它比它慢的常数因素,std::map并且实际上会更快地执行诸如查找、插入等操作。

如果你想使用std::mapand std::vector,没有什么能阻止你。如果你想将它们暴露给 Python,你必须自己包装它们。如果这种包装消耗了您希望节省的全部或大部分时间,请不要感到震惊。我不知道有任何工具可以为您自动执行此操作。

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

我的一般建议是

  • 如果你想要一个固定大小的数组(你谈论指定列表的大小),你实际上可能想要一个像 numpy 数组这样的东西。

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

  • dict实际上做得很好。我绝对不会尝试引入一种新的通用类型以在基于 的 Python 中使用std::map,它对于许多重要操作具有更差的算法复杂性,并且 - 至少在某些实现中 - 将一些优化留给dict已经拥有的用户。

    如果我确实想要一些更像 的东西std::map,我可能会使用数据库。如果我想存储在 a 中的dict东西(或者就此而言,我存储在 a 中的东西list)变得太大而让我无法舒适地存储在内存中,这通常是我所做的。Pythonsqlite3在标准库和所有其他可用主要数据库的驱动程序中都有。

于 2010-04-23T14:58:39.677 回答
9

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

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

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

并且 std::map 不应与 dict 进行比较;std::map 以排序顺序维护键,因为它是平衡树,dict 解决了不同的问题。更好的比较是 dict 和 Google 的哈希表。

于 2011-05-26T13:55:00.383 回答
3

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

于 2009-10-10T03:57:11.497 回答
0

没有办法让原生 Python 列表/字典达到 C++ 映射/向量甚至接近的速度。它与分配或类型声明无关,而是支付解释器开销。您提到的示例(numpy)是 C 扩展,正是出于这个原因,它是用 C 编写的。

于 2009-10-10T04:38:32.640 回答
0

只是因为这里没有提到:您可以轻松地将例如 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 端访问数据(有一些开销)。

于 2020-05-25T19:04:05.877 回答