如何以cython纯模式遍历列表

为了加快struct.pack()的速度,我将以下内容打包为一个int字节:

import cython as c
from cython import nogil,compile,returns,locals,cfunc,pointer,address

int_bytes_buffer = c.declare(c.char[400],[0] * 400)


@locals(i = c.int,num = c.int)
@returns(c.int)
@cfunc
@nogil
@compile
def int_to_bytes(num):
    i = 0
    while num >0:
        int_bytes_buffer[i] = num%256
        num//=256
        i+=1

    return int_bytes_buffer[0]


int_to_bytes(259)

我正在尝试使用以下错误代码将其用于int列表:

@locals(i = c.int,ints_p = pointer(c.int[100]),num = c.int)
@returns(c.int)
@cfunc
@nogil
@compile
def int_to_bytes(num):
    i = 0
    for num in ints_p:
        while num >0:
            int_bytes_buffer[i] = num%256
            num//=256
            i+=1

    return int_bytes_buffer[0]

ints = c.declare(c.int[100],[259]*100)
int_to_bytes(address(ints))

这给了我

    for num in ints_p:
              ^
----------------------------------------------------------

 accessing Python global or builtin not allowed without gil

很明显,我不应该使用in或在指针上循环。

如何在函数内部遍历list-made-array?

编辑

我试图将一个指向int数组的指针传递给该函数,并使其在没有gil的情况下工作,以便可以对其进行并行化。

该函数的参数应该是ints_p:

@locals(ints_p = pointer(c.int[100]),i = c.int,num = c.int)
@returns(c.int)
@cfunc
@nogil
@compile
def int_to_bytes(ints_p):
    i = 0
    for num in (*ints_p):
        while num >0:
            int_bytes_buffer[i] = num%256
            num//=256
            i+=1

    return int_bytes_buffer[0]

ints = c.declare(c.int[100],[259]*100)
int_to_bytes(address(ints))

我想检查实际的整数并打包(不包含gil)

编辑2

我知道struct.pack。我希望使用cython和nogil做一个可并行化的变体。

duracraft 回答:如何以cython纯模式遍历列表

这毫无意义:

  1. Python int可以任意大。在“打包”中的实际计算工作是在计算是否适合给定大小,然后将其复制到该大小的空间中。但是,您使用的是C个int数组。这些具有固定的大小。将它们提取到字节数组中基本上没有任何工作要做。您所做的只是编写了一个效率很低的memcpy版本。它们实际上已经作为一组连续的字节在内存中-您要做的就是这样查看它们:

    # using Numpy (no Cython)
    ints = np.array([1,2,3,4,5,6,7],dtype=np.int) # some numpy array already initialized
    as_bytes = ints.view(dtype=np.byte) # no data is copied - wonderfully efficient
    

    您可以使类似的方法也适用于另一个数组库或C数组:

    # slightly pointless use of pure-Python mode since this won't
    # be valid in Python.
    @cython.cfunc
    @cython.returns(cython.p_char)
    @cython.locals(x = cython.p_int)
    def cast_ptr(x):
        return cython.cast(cython.p_char,x)
    
  2. 您说您想要nogil,以便可以并行化。当需要进行实际的计算工作时,并行化会很好地工作。当任务受内存访问限制时,它将无法正常工作,因为线程往往最终会互相等待访问内存。该任务无法很好地并行化。

  3. 内存管理有问题。您只能写入固定大小的缓冲区。要分配大小可变的数组,您有多种选择:可以使用numpy或Python array模块(或类似模块)让Python负责内存管理,或者可以使用{{ 1}}和malloc来分配C级数组。由于您声称需要free,因此必须使用C方法。但是,您无法从Cython的纯Python模式执行此操作,因为所有内容也都必须在Python中工作,并且没有nogilmalloc的Python等效项。如果您坚持要进行这项工作,那么您就必须放弃Cython的纯Python模式,并使用标准的Cython语法,因为您尝试执行的操作无法与两者兼容。

    请注意,当前free是一个全局数组。这意味着多个线程将共享它-对于您认为的并行化来说是一场灾难。


您需要清楚地考虑输入的内容。如果它是Python整数的列表,则无法使用int_bytes_buffer进行此操作(因为您正在处理Python对象,因此需要GIL)。如果它是某些C级数组(例如Numpy,nogil模块或Cython声明的C数组),则您的数据已经具有所需的格式,则只需查看其格式即可。


编辑:从注释中看,这显然是XY问题(您要解决此Cython语法问题,因为您想打包一个整数列表)我添加了一种打包的快速方法使用Cython的Python int列表。这比struct pack快7倍,比将列表传递到array快5倍。它通常更快,因为它专门做一件事。

我使用array.array作为便捷的可写数据存储,并使用Python memoryview class(与Cython memoryview语法不太相同...)作为强制转换数据类型的方法。没有花费任何真正的精力来优化它,因此您可以进行改进。请注意,最后复制到bytearray不会更改可测量的时间,仅说明了复制内存与整体速度无关。

bytes
,

您的代码中有一些错误。

  1. 在错误Accessing Python global or builtin not allowed without gil中,因此您需要删除@nogil的标记。删除后,它不会显示错误。在我的代码中测试过。但是还有其他错误。

  2. 您的函数有一些问题。 def int_to_bytes(num):不应在函数中传递num,因为将在num循环中分配值for。我将其删除为def int_to_bytes():,该函数正常工作。但是仍然存在错误。

    @locals(i = c.int,ints_p = c.int(5),num = c.int)
    @returns(c.int)
    @cfunc
    @compile

    def int_to_bytes():
        ints_p = [1,5]
        i = 0
        for num in ints_p:
            while num >0:
                int_bytes_buffer[i] = num%256
                num//=256
                i+=1

        return int_bytes_buffer[1]

    a = int_to_bytes()
    print(a)
  1. 最后,我不明白您为什么将地址传递给函数,因为该函数不应该接受任何东西。

该代码对我有用:

import cython as c
from cython import nogil,compile,returns,locals,cfunc,pointer,address

int_bytes_buffer = c.declare(c.char[400],[0] * 400)

ints = c.declare(c.int[100],[259]*100)
# for i in list(*address(ints)):
#   print(i)
@locals(i = c.int,num = c.int)
@returns(c.int)
@cfunc
@compile

def int_to_bytes(values):
    i = 0
    for num in list(*address(values)):
        while num >0:
            int_bytes_buffer[i] = num%256
            num//=256
            i+=1

    return int_bytes_buffer

a = int_to_bytes(ints)
print([i for i in a])

希望有帮助。

本文链接:https://www.f2er.com/3063696.html

大家都在问