HSPポータル
サイトマップ お問い合わせ


HSPTV!掲示板


未解決 解決 停止 削除要請

2016
1220
配列の計算の高速化4解決


リンク

2016/12/20(Tue) 22:03:20|NO.77668

wav合成プログラムを作っており、簡略化していますが一部次のようなスクリプトを使います。
実際はNOTESやLENGはもっと大きな数となり、処理が結構重いです。
何か良い高速化の方法は無いでしょうか。

#define NOTES 1000 #define LENG 10000 #module #deffunc init ddim TMP_data,100000 return #deffunc addWave int _p1,int _p2 repeat _p1,_p2 TMP_data(cnt)+=1.0*rnd(100) TMP_data(cnt)=limitf(TMP_data(cnt),-32767.0,32767.0) loop return #global init repeat NOTES addWave LENG,rnd(10000) loop color :mes "OK" stop



この記事に返信する


スペース

リンク

2016/12/20(Tue) 22:50:01|NO.77669

#deffuncを使わないだけでも、少し高速化しませんか?
あとは・・・可能な限り無駄な計算や型の変換(影響あるかわかりませんが。)を減らすしか無いかと。



3k

リンク

2016/12/21(Wed) 00:38:59|NO.77670

見てる感じ、単純にデータ数が多すぎてHSPというスクリプト言語の処理速度が見えてしまっている感じですね。
これだけ単純な処理だとアルゴリズム的な改良は無理なため、スクリプトの謂わば隙を突いた書き方が必要になってきます。
つまり、実行しているHSPの気持ちになる、ということですね…^^;;

手元で計測結果を元に、色々やってみました。
結局、大した成果にはならなかったですが、参考程度に。

○ 元のスクリプトと同じ

#include "winmm.as" #define NOTES 1000 #define LENG 10000 ddim TMP_data,100000 timeGetTime : s = stat repeat NOTES repeat LENG,rnd(10000) TMP_data(cnt)+=1.0*rnd(100) TMP_data(cnt)=limitf(TMP_data(cnt),-32767.0,32767.0) loop loop timeGetTime : e = stat color :mes strf("OK : %d milliseconds", e-s)
スクリプト短くしたかったので#deffunc削ってみました、3150msくらい。
ちなみに#deffuncを削ったところで処理は変わらずでした。
NOTESの数が1000のオーダーなので、10000のLENGのオーダーに比べてほぼ差がなかったっぽいです。

◆型変換をなくす
スペースさんも仰っている方法ですが、自動でされる型変換は敢えて書かない方が早いです。
HSPは左の項に演算結果の型が合う(A+Bの結果はA)のと、A+=Bなどの自己代入は擬似的にはA=A+Bと同じなのを考えると、

これは

TMP_data(cnt)+=1.0*rnd(100)

これと同じ。

TMP_data(cnt)+=rnd(100)

これだけかよ、と思うかもしれませんがこれで2900msぐらい、なんと5%ぐらい削れる。

◆添字計算、代入を減らす
あんまり意識してないかもしれませんが、”A(cnt)”という記述は”Aという変数のcnt番目を取り出す”という処理なので、これだけで実は計算が走ります。

下らないかもしれませんが、これが

TMP_data(cnt)+=rnd(100) TMP_data(cnt)=limitf(TMP_data(cnt),-32767.0,32767.0)

こうした方が早い、という理屈分かりますか?

t=TMP_data(cnt)+rnd(100) TMP_data(cnt)=limitf(t,-32767.0,32767.0)
これで2750msくらい、更に同じく5%いかないぐらい削れる。

そして、この一時変数 t への代入、全くもって無駄でlimitfの中にしまえるの分かりますよね。

TMP_data(cnt)=limitf(TMP_data(cnt)+rnd(100),-32767.0,32767.0)
これで2350msくらい、更に10%ぐらい削れて、思ったより変数への代入が時間食っていることがわかったりします。

◆更に代入を添字計算をなくす
上述までで気付いたかもしれませんが、TMP_data(cnt)への読み込みと代入が減るとそれなりに効いてきたりします。
ところで、”NOTES個の、LENGの長さの波を足す”という処理ですが、加算は順序を好きに並べ替えていいので、このループの入れ子関係を逆にすると早くできるの、分かりますか?

例えば、提示して頂いた例だと(ランダム)オフセットが入ってたりランダム値加算なので実は無理なんですが、もし下記のような感じに書き下せるならば

repeat NOTES repeat LENG TMP_data(cnt)=limitf(TMP_data(cnt)+rnd(100),-32767.0,32767.0) loop loop

こう書き直せて

repeat LENG gcnt = cnt repeat NOTES TMP_data(gcnt)=limitf(TMP_data(gcnt)+rnd(100),-32767.0,32767.0) loop loop

配列アクセスが内部のループでは固定されているため、一時変数に逃がせて

repeat LENG gcnt=cnt t=TMP_data(gcnt) repeat NOTES t=limitf(t+rnd(100),-32767.0,32767.0) loop TMP_data(gcnt)=t loop
これで2350ms → 1800msまで削れます、下らないと思うかもしれませんが、実行しているHSPの気持ちになると確かに速くなる道理はあったりします。
上の変形するとgcntという一時変数も消せたりしますが、cntの読み込みは確か通常の変数より若干遅かった記憶があるので(今は違うかも?)、上の形の方が速いかもしれません。

あと、演算精度が int で十分なら、double は演算の仕方がちょっと特殊なので int にすると早くなったりします。

repeat LENG gcnt=cnt t=int(TMP_data(gcnt)) repeat NOTES t=limit(t+rnd(100),-32767,32767) loop TMP_data(gcnt)=double(t) loop

以上、私自身はこういった最適化すると徒に読みにくくなるのでお勧めはしませんが…。
どうしてもHSPで速くしたい場合は基本的に”変数の読み込み・書き込み”に気を使うと良いと思います。



MillkeySoftware

リンク

2016/12/21(Wed) 18:33:50|NO.77680

処理の高速化は、スペースさん・3k さんの言う通りに、単純に計算量の違いだからねー。
一番わかりやすいのが、変数の型のサイズ

INT は 32Bit なので、単純に 32回の信号。
DOUBLE は 64Bit なので、単純に 64回の信号。

この時点で、差があるので、単純に INT の方が早いとなる。



リンク

2016/12/21(Wed) 22:00:25|NO.77683

方々、ご丁寧な検証解説ありがとうございました。
やはりアルゴリズム方面での改良は難しいですね。
計算量に注意することで結構な差が出そうなので参考にさせていただきたいと思います。
簡略化していないソースはこんなんですが頑張ってみます。

#deffunc addWave double Frequency,int _p1,int _p2 add_init _p1,_p2 frqPlus=Frequency*HMsample/FH_SamplingRate AMP=Amplification*noteVol*32767.0/128 repeat Time2,live2 val =HMDat(int(frqCnt)\HMsample) TMP_data(cnt)+=AMP*val* addEG(cnt-live2)*addLFOV(cnt-live2) TMP_data(cnt)=limitf(TMP_data(cnt),-32767.0,32767.0) frqCnt+=frqPlus*addLFOH(cnt-live2) loop NoteEnd+=Time3 chkWrited return



ONION software Copyright 1997-2021(c) All rights reserved.