【问题标题】:Cythonize two small numpy functions, help neededCythonize 两个小的 numpy 函数,需要帮助
【发布时间】:2013-12-03 04:43:06
【问题描述】:

问题

我正在尝试 Cythonize 两个小函数,这些函数主要处理 numpy ndarray 以用于某些科学目的。这两个小函数在遗传算法中被调用了数百万次,占算法花费的大部分时间。

我自己取得了一些进展,并且两者都运行良好,但我只获得了微小的速度提升 (10%)。更重要的是,cython --annotate 表明大部分代码仍在通过 Python。

代码

第一个函数:

这个函数的目的是取回数据片段,它在一个内部嵌套循环中被调用了数百万次。根据 data[1][1] 中的布尔值,我们可以正向或反向获取切片。

#Ipython notebook magic for cython
%%cython --annotate
import numpy as np
from scipy import signal as scisignal

cimport cython
cimport numpy as np
def get_signal(data):
    #data[0] contains the data structure containing the numpy arrays
    #data[1][0] contains the position to slice
    #data[1][1] contains the orientation to slice, forward = 0, reverse = 1

    cdef int halfwinwidth = 100
    cdef int midpoint = data[1][0]
    cdef int strand = data[1][1]
    cdef int start = midpoint - halfwinwidth
    cdef int end = midpoint + halfwinwidth
    #the arrays we want to slice
    cdef np.ndarray r0 = data[0]['normals_forward']
    cdef np.ndarray r1 = data[0]['normals_reverse']
    cdef np.ndarray r2 = data[0]['normals_combined']
    if strand == 0:
        normals_forward = r0[start:end]
        normals_reverse = r1[start:end]
        normals_combined = r2[start:end]
    else:
        normals_forward = r1[end - 1:start - 1: -1]
        normals_reverse = r0[end - 1:start - 1: -1]
        normals_combined = r2[end - 1:start - 1: -1]
    #return the result as a tuple
    row = (normals_forward,
           normals_reverse,
           normals_combined)
    return row

第二个功能

这个得到一个 numpy 数组的元组列表,我们希望将数组元素明智地相加,然后对它们进行归一化并获得交集的积分。

def calculate_signal(list signal):
    cdef int halfwinwidth = 100
    cdef np.ndarray profile_normals_forward = np.zeros(halfwinwidth * 2, dtype='f')
    cdef np.ndarray profile_normals_reverse = np.zeros(halfwinwidth * 2, dtype='f')
    cdef np.ndarray profile_normals_combined = np.zeros(halfwinwidth * 2, dtype='f')
    #b is a tuple of 3 np.ndarrays containing 200 floats
    #here we add them up elementwise
    for b in signal:
        profile_normals_forward += b[0]
        profile_normals_reverse += b[1]
        profile_normals_combined += b[2]
    #normalize the arrays
    cdef int count = len(signal)

    #print "Normalizing to number of elements"
    profile_normals_forward /= count
    profile_normals_reverse /= count
    profile_normals_combined /= count
    intersection_signal = scisignal.detrend(np.fmin(profile_normals_forward, profile_normals_reverse))
    intersection_signal[intersection_signal < 0] = 0
    intersection = np.sum(intersection_signal)

    results = {"intersection": intersection,
               "profile_normals_forward": profile_normals_forward,
               "profile_normals_reverse": profile_normals_reverse,
               "profile_normals_combined": profile_normals_combined,
               }
    return results

感谢任何帮助 - 我尝试使用内存视图,但由于某种原因,代码变得非常非常慢。

【问题讨论】:

  • cdef np.ndarray 并没有带来很大的加速。您必须指定 dtype 才能获得真正的提升。 (不过,该语法已被弃用。)
  • 如果函数 1 被调用数百万次,你应该使用 cdef 这个函数并将调用这个函数的例程也放在 Cython 中......另一个问题可能是 data 是一本字典,我不确定这是否是获得真正改进的最佳方法。或许您应该使用ndarray 代替data
  • 新的语法类型是内存视图吗?因为我尝试将 cdef float [:] 作为一种类型,但由于某种原因性能下降了 300%
  • 指定类型,使用 C 类型而不是 Python 类型。顺便说一句,numba 可能也不错。
  • 这可能是指出numba 的好时机。 Numba 是 python/numpy 的 JIT 编译器,在我的测试中,自动编译功能与优化的 cython 大致相当。

标签: python arrays numpy cython memoryview


【解决方案1】:

修复数组 cdef 后(如前所述,指定了 dtype),您应该将例程放入 cdef 函数中(只能由同一脚本中的 def 函数调用)。

在函数的声明中,您需要提供类型(如果它是 numpy 数组,还需要提供维度):

cdef get_signal(numpy.ndarray[DTYPE_t, ndim=3] data):

我不确定使用 dict 是个好主意。您可以使用 numpy 的列或行切片,例如 data[:, 0]。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-09-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-11
    • 2011-12-06
    • 2023-04-09
    相关资源
    最近更新 更多