gpt4 book ai didi

python - 优化我的 Cython/Numpy 代码?目前只有 30% 的性能提升

转载 作者:太空宇宙 更新时间:2023-11-03 12:51:30 25 4
gpt4 key购买 nike

有什么我忘记做的事情来加快速度吗?我正在尝试实现一种算法,该算法在名为 Tuning Timbre Spectrum Scale 的书中描述。另外---如果一切都失败了,我有没有办法只用 C 编写这部分代码,然后能够从 python 调用它?

import numpy as np
cimport numpy as np

# DTYPE = np.float
ctypedef np.float_t DTYPE_t

np.seterr(divide='raise', over='raise', under='ignore', invalid='raise')

"""
I define a timbre as the following 2d numpy array:
[[f0, a0], [f1, a1], [f2, a2]...] where f describes the frequency
of the given partial and a is its amplitude from 0 to 1. Phase is ignored.
"""

#Test Timbre
# cdef np.ndarray[DTYPE_t,ndim=2] t1 = np.array( [[440,1],[880,.5],[(440*3),.333]])

# Calculates the inherent dissonance of one timbres of the above form
# using the diss2Partials function
cdef DTYPE_t diss1Timbre(np.ndarray[DTYPE_t,ndim=2] t):
cdef DTYPE_t runningDiss1
runningDiss1 = 0.0
cdef unsigned int len = np.shape(t)[0]
cdef unsigned int i
cdef unsigned int j
for i from 0 <= i < len:
for j from i+1 <= j < len:
runningDiss1 += diss2Partials(t[i], t[j])
return runningDiss1

# Calculates the dissonance between two timbres of the above form
cdef DTYPE_t diss2Timbres(np.ndarray[DTYPE_t,ndim=2] t1, np.ndarray[DTYPE_t,ndim=2] t2):
cdef DTYPE_t runningDiss2
runningDiss2 = 0.0
cdef unsigned int len1 = np.shape(t1)[0]
cdef unsigned int len2 = np.shape(t2)[0]
runningDiss2 += diss1Timbre(t1)
runningDiss2 += diss1Timbre(t2)
cdef unsigned int i1
cdef unsigned int i2
for i1 from 0 <= i1 < len1:
for i2 from 0 <= i2 < len2:
runningDiss2 += diss2Partials(t1[i1], t2[i2])
return runningDiss2

cdef inline DTYPE_t float_min(DTYPE_t a, DTYPE_t b): return a if a <= b else b

# Calculates the dissonance of two partials of the form [f,a]
cdef DTYPE_t diss2Partials(np.ndarray[DTYPE_t,ndim=1] p1, np.ndarray[DTYPE_t,ndim=1] p2):
cdef DTYPE_t f1 = p1[0]
cdef DTYPE_t f2 = p2[0]
cdef DTYPE_t a1 = abs(p1[1])
cdef DTYPE_t a2 = abs(p2[1])

# In order to insure that f2 > f1:
if (f2 < f1):
(f1,f2,a1,a2) = (f2,f1,a2,a1)

# Constants of the dissonance curves
cdef DTYPE_t _xStar
_xStar = 0.24
cdef DTYPE_t _s1
_s1 = 0.021
cdef DTYPE_t _s2
_s2 = 19
cdef DTYPE_t _b1
_b1 = 3.5
cdef DTYPE_t _b2
_b2 = 5.75

cdef DTYPE_t a = float_min(a1,a2)
cdef DTYPE_t s = _xStar/(_s1*f1 + _s2)
return (a * (np.exp(-_b1*s*(f2-f1)) - np.exp(-_b2*s*(f2-f1)) ) )

cpdef dissTimbreScale(np.ndarray[DTYPE_t,ndim=2] t,np.ndarray[DTYPE_t,ndim=1] s):
cdef DTYPE_t currDiss
currDiss = 0.0;
cdef unsigned int i
for i from 0 <= i < s.size:
currDiss += diss2Timbres(t, transpose(t,s[i]))
return currDiss

cdef np.ndarray[DTYPE_t,ndim=2] transpose(np.ndarray[DTYPE_t,ndim=2] t, DTYPE_t ratio):
return np.dot(t, np.array([[ratio,0],[0,1]]))

代码链接:Cython Code

最佳答案

以下是我注意到的一些事情:

  1. 使用 t1.shape[0] 而不是 np.shape(t1)[0] 等等。
  2. 不要将 len 用作变量,因为它是 Python 中的内置函数(不是为了速度,而是为了良好的实践)。使用 L 或类似的东西。
  3. 除非确实需要,否则不要将二元数组传递给函数。每次传递数组时,Cython 都会检查缓冲区。因此,当使用 diss2Partials(t[i], t[j]) 时,执行 diss2Partials(t[i,0], t[i,1], t[j,0], t[j,1]) 并适本地重新定义 diss2Partials
  4. 不要使用 abs,或者至少不要使用 Python。它必须将 C double 转换为 Python float,调用 abs 函数,然后再转换回 C double。像使用 float_min 那样创建一个内联函数可能会更好。
  5. 调用 np.exp 与使用 abs 类似。将 np.exp 更改为 exp 并将 from libc.math cimport exp 添加到顶部的导入。
  6. 完全摆脱 transpose 功能。 np.dot 确实减慢了速度,但这里确实不需要矩阵乘法。重写您的 dissTimbreScale 函数以创建一个空矩阵,比如 t2。在当前循环之前,将 t2 的第二列设置为等于 t 的第二列(最好使用循环,但你可能会逃避 Numpy 操作这里)。然后,在当前循环内,放入一个循环,将 t2 的第一列设置为 t 的第一列乘以 s[i]。这就是你的矩阵乘法真正在做的事情。然后将 t2 作为第二个参数传递给 diss2Timbres 而不是 transpose 函数返回的参数。

先做 1-5,因为它们相当简单。数字 6 可能需要更多的时间、精力和实验,但我怀疑它也可以显着提高速度。

关于python - 优化我的 Cython/Numpy 代码?目前只有 30% 的性能提升,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5334012/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com