3

物事を少しスピードアップするためにここで忘れたことはありますか? 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 コード

4

3 に答える 3

9

ここに私が気づいたいくつかのことがあります:

  1. 他の場所では and のt1.shape[0]代わりに使用します。np.shape(t1)[0]
  2. lenこれは Python の組み込み関数であるため、変数として使用しないでください(速度のためではなく、良い習慣のためです)。L などを使用します。
  3. 本当に必要でない限り、2 要素の配列を関数に渡さないでください。Cython は、配列を渡すたびにバッファーをチェックします。そのため、代わりにdiss2Partials(t[i], t[j])doを使用し、適切に再定義してください。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.expexpて追加from libc.math cimport expします。
  6. transpose関数を完全に削除します。はnp.dot本当に速度を落としていますが、ここでは行列の乗算は必要ありません。dissTimbreScale空の行列を作成するように関数を書き直してくださいt2。現在のループの前に、 の 2 番目の列を の 2 番目の列t2と等しくなるように設定しtます (できればループを使用しますが、ここではおそらく Numpy 操作で回避できます)。次に、現在のループ内に、timest2の最初の列と等しい最初の列を設定するループを挿入します。それが、行列の乗算が実際に行っていたことです。次に、関数によって返されるものの代わりに、2 番目のパラメーターとして渡すだけです。ts[i]t2diss2Timbrestranspose

比較的簡単なので、最初に 1 ~ 5 を実行します。6 番は、もう少し時間と労力がかかり、実験も必要かもしれませんが、速度が大幅に向上するのではないかと思います。

于 2011-03-24T18:38:41.597 に答える
0

どの関数が最も時間がかかるかを確認するために、コードをプロファイリングします。その場合はdiss2Timbres、パッケージ「numexpr」を利用できます。

関数の 1 つについて Python/Cython と Numexpr を比較しました ( SO へのリンク)。配列のサイズにもよりますが、numexpr は Cython と Fortran の両方を上回りました。

注:この投稿は本当に古いことがわかりました...

于 2015-02-13T12:56:31.450 に答える
0

あなたのコードで:

for i from 0 <= i < len:
    for j from i+1 <= j < len:
        runningDiss1 += diss2Partials(t[i], t[j])
return runningDiss1

境界チェックは配列ルックアップごとに実行され@cython.boundscheck(False)、関数の前にデコレータを使用してから、インデックスとして i と j を使用する前に unsigned int 型にキャストします。詳細については、Numpy チュートリアルのcython を参照してください。

于 2011-03-17T12:50:16.960 に答える