我有一个时间关键模型,我在Cython中写道.我的Cython扩展的主要功能有一个循环,根据Cython分析器(它显示黄色阴影的Python调用量),唯一的"黄色"部分是我正在附加到Python列表的地方.(我必须输出一个Python对象,因为我在Python脚本中调用我的Cython函数).这是我的函数的基本思想(其余的是多余的,我已经测试了这个函数的每个部分,并且追加操作是瓶颈):
from libc.math cimport log
def main(some args):
cdef (some vars)
cdef list OutputList = []
# NB: all vars have declared types
for x in range(t):
(do some Cythonic stuff, some of which uses my cimport-ed log)
if condition is True:
OutputList.append(x) # this is the only 'yellow' line in my main loop.
return OutputList # return Python object to Python script that calls main()
Run Code Online (Sandbox Code Playgroud)
不幸的是,我不知道输出数组/列表/向量的长度(无论我最终使用什么).但是,我可以将它设置为52560,这就是我最终在其他Python代码中将其调整到最后.我想在没有设置输出数组长度的情况下获得主要的速度提升,但如果它阻止我,我会很乐意抛出这个希望.
我也试过在Cython中使用C++来使用C++数据结构(向量,队列等),但这样做会消除我很好地保存日志的能力.我在Cython文档/ wiki上看到你可以编写一个'shim'模块来在C++ Cython中使用pure-C函数,但我不知道如何做到这一点,我找不到任何关于如何去做的事情.
无论如何,我欢迎所有坚持我的问题的建议:
在Cython中构建未知大小的列表/数组/向量的最佳方法是什么?或者是否有一个明确的替代方案(例如使用已知长度的可迭代对象进行解决)会使我的未知长度问题变得无法解决?
更新
C++容器确实显示了超过项目分配的速度增加,并且项目分配确实显示速度增加而不是附加到列表和numpy数组.最好的方法是使用C++容器,同时还能够使用纯C函数...这可以防止减速不得超越libc.math以获得快速日志功能.
#cython: boundscheck=False, wraparound=False
from libc.math cimport log
from cython.parallel cimport prange
import numpy as pynp
cimport numpy as np
# copy declarations from libcpp.vector to allow nogil
cdef extern from "<vector>" namespace "std":
cdef cppclass vector[T]:
void push_back(T&) nogil
size_t size()
T& operator[](size_t)
def makearray(int t):
cdef vector[np.float_t] v
cdef int i
with nogil:
for i in range(t):
if i % 10 == 0:
v.push_back(log(i+1))
cdef np.ndarray[np.float_t] a = pynp.empty(v.size(), dtype=pynp.float)
for i in prange(a.shape[0], nogil=True):
a[i] = v[i]
return a
Run Code Online (Sandbox Code Playgroud)
第二部分大约是第一个循环的 1%,因此在这种情况下优化它的速度是没有意义的。
<math.h>extern "C" { ... }我的系统上有,所以libc.math.log可以工作。
PyArray_SimpleNewFromData()可用于避免复制数据,以节省您自己管理阵列内存的成本。
追加 python 列表是 CPython 中一项经过良好优化的操作。Python 不会为每个元素分配内存,而是增量增长指向列表中对象的指针数组。因此,仅仅切换到 Cython 对您没有多大帮助。
您可以在 Cython 中使用 C++ 容器,如下所示:
from libc.math cimport log
from libcpp.list cimport list as cpplist
def main(int t):
cdef cpplist[int] temp
for x in range(t):
if x> 0:
temp.push_back(x)
cdef int N = temp.size()
cdef list OutputList = N*[0]
for i in range(N):
OutputList[i] = temp.front()
temp.pop_front()
return OutputList
Run Code Online (Sandbox Code Playgroud)
您必须测试这是否会加快速度,但也许您不会获得太多速度。
另一种方法是使用 numpy 数组。这里Cython在优化代码方面非常擅长。OutputList因此,如果您可以使用 numpy 数组作为 main 的返回值,则应该考虑这一点,并通过分配和填充 numpy 数组的某些 Cython 代码来替换构造和填充。
有关更多信息,请参阅http://docs.cython.org/src/tutorial/numpy.html
询问您是否需要帮助。
更新:如果避免在两个循环中进行方法查找,代码应该会更快一些:
from libc.math cimport log
from libcpp.list cimport list as cpplist
def main(int t):
cdef cpplist[int] temp
push_back = temp.push_back
for x in range(t):
if x> 0:
push_back(x)
cdef int N = temp.size()
cdef list OutputList = N*[0]
front = temp.front()
pop_front = temp.pop_front()
for i in range(N):
OutputList[i] = front()
pop_front()
return OutputList
Run Code Online (Sandbox Code Playgroud)