【问题标题】:How to declare 2D list in Cython如何在 Cython 中声明 2D 列表
【发布时间】:2013-02-05 04:38:04
【问题描述】:

我正在尝试编译这种代码:

def my_func(double c, int m):
    cdef double f[m][m]

    f = [[c for x in range(m)] for y in range(m)]
    ...

引发:

Error compiling Cython file:
------------------------------------------------------------
def grow(double alpha, double beta, double gamma, int m, int s):
    cdef double f[m][m]
                     ^
------------------------------------------------------------
test.pyx:6:22: Not allowed in a constant expression

之后我假设我不能在指定的地方使用变量,我尝试使用数值:

def my_func(double c, int m):
    cdef double f[500][500]

    f = [[c for x in range(500)] for y in range(500)]
    ...

然后我得到:

Error compiling Cython file:
------------------------------------------------------------
    f = [[beta for x in range(500)] for y in range(500)]
     ^
------------------------------------------------------------
test.pyx:13:6: Assignment to non-lvalue 'f'

所以,我想知道如何在 cython 代码中声明和制作 2D 列表。我在谷歌搜索“cython 2D list”的文档中找不到这种示例

【问题讨论】:

  • 好吧,如果我离开声明,我会得到编译的代码,所以我想我的声明是错误的
  • 你真的想要一个列表列表,还是一个二维 C 数组?
  • 是的,就像写的一样。我正在尝试加速非常慢的 Python 代码,该代码循环遍历此(以及另外两个)列表的每个元素。想象一下这有多慢。
  • 这是一个非此即彼的问题。您声明了一个 2d C 数组,但使用 Python 列表来初始化 f,所以我试图找出您是否混淆了两者(或者甚至不知道差异,正如您的语言所指示的那样)或想要一个特定的一个,只是使用了错误的语法。
  • 是的,学习 Cython :) 在文档中我看到了对象,在我看来是 Python 列表,声明为 p[1000],所以我想我应该这样声明列表。我应该尝试声明还是不需要声明列表?我在这里看到了这个例子:docs.cython.org/src/userguide/tutorial.html#primes

标签: python cython


【解决方案1】:

不要在 Cython 中使用列表推导。没有加速,因为他们创建了常规的 python 列表。 Wiki says,您应该在 Cython 中使用动态分配,如下所示:

from libc.stdlib cimport malloc, free

def my_func(double c, int m):
    cdef int x
    cdef int y
    cdef double *my_array = <double *>malloc(m * m * sizeof(double))

    try:

        for y in range(m):
            for x in range(m):
                #Row major array access
                my_array[ x + y * m ] = c

        #do some thing with my_array

    finally:
       free( my_array )

但是如果你需要一个二维数组的python对象,推荐使用NumPy

【讨论】:

  • 感谢您的 sn-p,但当我看到 malloc 和类似术语时,我只是害怕。我还没有,用 f2py 对我来说更容易,但想从 Cython 入手很久了。正如我回答的那样,我现在将尝试使用 Cython 和 Numpy 数组而不是 Cython 和 Python 列表。谢谢
【解决方案2】:
cdef double f[500][500]

这是声明一个包含 500 个 C 数组的 C 数组,该数组包含 500 个双精度数。那是 500 * 500 打包的双精度值(在这种情况下存储在堆栈中,除非 Cython 做了一些时髦的事情),没有任何间接性,这有助于性能和缓存利用率,但显然增加了严格的限制。也许你想要这个,但你应该先学习足够的 C 来了解这意味着什么。顺便说一句,一个限制是大小必须是编译时常量(取决于 C 版本;C99 和 C10 允许),这就是第一条错误消息的含义。

如果您确实使用数组,则不会像以前那样初始化f,因为这没有任何意义。 f 已经是 500x500 双变量,并且不能将整个数组分配给(这是后面的错误消息试图告诉您的内容)。特别是,列表理解创建了一个完整的 Python 列表对象(您也可以从 Cython 使用,见下文),其中包含完整的“盒装”Python 对象(在本例中为float 对象)。列表与 C 数组不兼容。使用带有项目分配的嵌套for 循环进行初始化。最后,这样的数组需要 500 * 500 * 8 字节,几乎是 2 MiB。在某些系统上,这比整个堆栈都大,而在所有其他系统上,它占堆栈的很大一部分,这是一个坏主意。您应该堆分配该数组。

如果您使用 Python 列表,请注意,您不会在性能和内存使用方面获得太多改进(假设您的代码将主要操作该列表),但您可能会获得一些便利作为回报。你可以不用cdef,或者使用list作为类型(object应该也可以,但是你没有任何收获,所以你最好省略它)。

NumPy 数组可能更快、更节省内存、并且更方便使用。如果您可以根据 NumPy 的操作来实现算法的性能关键部分,那么您可能会在不使用 Cython 的情况下获得所需的加速

【讨论】:

  • 感谢您的解释。我想我应该找一本 C 书,同样的建议只是通过浏览 Cython 文档。因此,在我了解更多有关 C 类型和操作的信息之前,我只在此示例中使用列表。我假设 C 数组应该更有效,但是它们不能从 Cython 函数返回到 Python 代码,而只能在 Cython 代码中使用?加速比目前只有 3 倍,但我会尝试更进一步,首先使用 Numpy 和 Cython,因为仅使用 Numpy 数组而不是列表不会提高此示例代码的性能,如果它是为了相信的话。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-05-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-14
相关资源
最近更新 更多