非常慢的 Numpy 缓冲区指针访问
Very slow Numpy buffer pointer access
我正在尝试获取指向 Numpy 数组的指针,以便我可以在我的 Cython 代码中快速操作它。我找到了两种获取缓冲区指针的方法,一种使用 array.__array_interface__['data'][0]
,另一种使用 array.ctypes.data
。他们都慢得令人痛苦。
我创建了一个小型 Cython class,它只是创建一个 numpy 数组并将指针存储到其缓冲区:
cdef class ArrayHolder:
cdef array
cdef long *ptr
def __init__(ArrayHolder self, allocate=True):
self.array = np.zeros((4, 12,), dtype=np.int)
cdef long ptr = self.array.__array_interface__['data'][0]
self.ptr = <long *>ptr
然后,回到 Python,我创建了这个 class 的多个实例,如下所示:
for i in range(1000000):
holder = ArrayHolder()
这大约需要 3.6 秒。使用 array.ctypes.data
是半秒 慢 .
当我再次注释掉对 __array_instance__['data']
和 运行 代码的调用时,它会在大约 1 秒内完成。
为什么获取Numpy数组缓冲区的地址这么慢?
我猜,这是某种延迟加载。当您第一次访问它时,Numpy 只会在 table 上执行 memset()
。我会尝试创建这个数组而不用零填充它来赢得时间。
这是我的测试:
import numpy as np
cdef class ArrayHolder:
cdef array
cdef long *ptr
def __init__(ArrayHolder self, allocate=True):
self.array = np.zeros((4, 12,), dtype=np.int)
def ptr(ArrayHolder self):
cdef long ptr = self.array.__array_interface__['data'][0]
from timeit import timeit
from cyth import ArrayHolder
print(timeit("ArrayHolder()", number=1000000, setup="from cyth import ArrayHolder"))
print(timeit("ArrayHolder().ptr()", number=1000000, setup="from cyth import ArrayHolder"))
$ python test.py
1.0442328620702028
3.4246508290525526
这可以通过使用 Cython 的静态类型机制来提供很大帮助。这样 Cython 就知道您正在处理的是适当类型的数组数组,并且可以生成优化的 C 代码。
cimport numpy as np # just so it knows np.int_t
cdef class ArrayHolder:
cdef np.int_t[:,:] array # now specified as a specific array type
cdef np.int_t *ptr # note I've changed this to match the array type
def __init__(ArrayHolder self, allocate=True):
self.array = np.zeros((4, 12,), dtype=np.int)
self.ptr = &self.array[0,0] # location of the first element
在此版本中,分配 self.array
时会产生少量成本,以检查该对象是否实际上是一个数组。但是,元素查找和获取地址现在与使用 C 指针一样快。
在你的旧版本中它是一个任意的 python 对象,所以有一个字典查找 __array_instance__
,一个字典查找 __getitem__
允许字典查找 data
。 __getitem__
的进一步字典查找允许您找到索引 0.
需要注意的一件事:如果您使用 cdef
告诉 Cython 数组类型,您可以直接在数组上进行所有索引,并且它的类型速度与使用指针,因此您可以完全跳过创建指针(除非您需要它传递给外部 C 代码)。 Turn off boundscheck
and wraparound
最后一点点速度。
我正在尝试获取指向 Numpy 数组的指针,以便我可以在我的 Cython 代码中快速操作它。我找到了两种获取缓冲区指针的方法,一种使用 array.__array_interface__['data'][0]
,另一种使用 array.ctypes.data
。他们都慢得令人痛苦。
我创建了一个小型 Cython class,它只是创建一个 numpy 数组并将指针存储到其缓冲区:
cdef class ArrayHolder:
cdef array
cdef long *ptr
def __init__(ArrayHolder self, allocate=True):
self.array = np.zeros((4, 12,), dtype=np.int)
cdef long ptr = self.array.__array_interface__['data'][0]
self.ptr = <long *>ptr
然后,回到 Python,我创建了这个 class 的多个实例,如下所示:
for i in range(1000000):
holder = ArrayHolder()
这大约需要 3.6 秒。使用 array.ctypes.data
是半秒 慢 .
当我再次注释掉对 __array_instance__['data']
和 运行 代码的调用时,它会在大约 1 秒内完成。
为什么获取Numpy数组缓冲区的地址这么慢?
我猜,这是某种延迟加载。当您第一次访问它时,Numpy 只会在 table 上执行 memset()
。我会尝试创建这个数组而不用零填充它来赢得时间。
这是我的测试:
import numpy as np
cdef class ArrayHolder:
cdef array
cdef long *ptr
def __init__(ArrayHolder self, allocate=True):
self.array = np.zeros((4, 12,), dtype=np.int)
def ptr(ArrayHolder self):
cdef long ptr = self.array.__array_interface__['data'][0]
from timeit import timeit
from cyth import ArrayHolder
print(timeit("ArrayHolder()", number=1000000, setup="from cyth import ArrayHolder"))
print(timeit("ArrayHolder().ptr()", number=1000000, setup="from cyth import ArrayHolder"))
$ python test.py
1.0442328620702028
3.4246508290525526
这可以通过使用 Cython 的静态类型机制来提供很大帮助。这样 Cython 就知道您正在处理的是适当类型的数组数组,并且可以生成优化的 C 代码。
cimport numpy as np # just so it knows np.int_t
cdef class ArrayHolder:
cdef np.int_t[:,:] array # now specified as a specific array type
cdef np.int_t *ptr # note I've changed this to match the array type
def __init__(ArrayHolder self, allocate=True):
self.array = np.zeros((4, 12,), dtype=np.int)
self.ptr = &self.array[0,0] # location of the first element
在此版本中,分配 self.array
时会产生少量成本,以检查该对象是否实际上是一个数组。但是,元素查找和获取地址现在与使用 C 指针一样快。
在你的旧版本中它是一个任意的 python 对象,所以有一个字典查找 __array_instance__
,一个字典查找 __getitem__
允许字典查找 data
。 __getitem__
的进一步字典查找允许您找到索引 0.
需要注意的一件事:如果您使用 cdef
告诉 Cython 数组类型,您可以直接在数组上进行所有索引,并且它的类型速度与使用指针,因此您可以完全跳过创建指针(除非您需要它传递给外部 C 代码)。 Turn off boundscheck
and wraparound
最后一点点速度。