2007年9月1日土曜日

なんですか、もう

C#で組んだシミュレーションプログラムをC++に移植し始めて約1週間。ようやく終わりが見えてきました。
というか、デバッグデバッグで、ようやく結果らしい結果が出たってとこですね。それでもまだ正しい結果を吐いてくれないので地道にチェックして行かなくてはいけませんがOrz
単純にアルゴリズムを移植しているだけの部分が半分、根本的に書き換えたところが半分ってところなので、割とコーディングはスッキリしたけれどまだ把握できていない部分もあったりします。とりあえず、FFTWは非常に優秀ですね。
にしても、久々にC++で組むとほんとケアレスミス連発しちゃいますね・・・。今回やっちまったミスのうち9割がバッファオーバーフローなどのメモリの扱い。C#と違って配列のインデックスをオーバーしてもC++はなんも言いませんからねぇ・・・。おかげで踏み越えるわ踏み越えるわで大変でした。書き換えなんて起こらないはずの関数を抜けたら変数のアドレスが訳分からん数字に書き換えられてるとかほんと悪夢だった。まぁ、アドレスが書き換わっていたために100%バッファオーバーフローの類だろうなと気付けただけマシなバグですが。今もまだプログラムが完全には動いていないので、どっかにメモリ周りのバグが潜んでいそうで怖い。しっかと潰して行かなくては。
今回一番やらかしたのはmemset関数とmemcpy関数の扱い。両方とも引数にバッファサイズを要求してくるのですが、処理したいバッファのバイト数を与えなくてはいけないので、配列長*単位サイズを計算しないと行けないんですね。が、ついつい配列長だけを渡してしまってコピーや値の設定が正しく行われず、結果が狂うと言うことが多々ありました。sizeof関数とかC#ではほとんど使わないからなぁ・・・。まぁそもそもmemsetとmemcpyに該当するモノが無かった気がするけど^^;
ここまでミスするんならいっそのことmemcpyとmemsetはラッパークラス作った方がいいんじゃないかという気がしてきた。C++って変数の型情報取得とかできたっけなぁ・・・?できたとして、それをsizeofの引数に渡せるのかしら?
まーそれよかsizeofの癖を付けた方がマシか^^;
さらに、FFTWの扱いも失敗してた。
forwardFFT = fftw_plan_dft_r2c_1d(length, doubleArray, complexArray, flags);
backwardFFT = fftw_plan_dft_c2r_1d(length, complexArray, doubleArray, flags);
といった形で1次元DFTと1次元IDFTのプランを生成し、それぞれのメソッドを持たせたFFTクラスを作った訳なんですが(超単純なラッパークラス)、このときマニュアルをちゃんと読んでなくてですね、実数配列のサイズと複素数配列のサイズの扱いの違いを理解していなかったんです^^;
実数配列をDFTすれば、その結果は複素数となります。が、その複素数の配列は実際にはちょうど真ん中から折り返すような値の配置になります。つまるところ、実数配列のサイズがNのDFTを行うと、本来ならNサイズの複素配列を必要とするところをN/2+1で表現可能というわけです(Nは整数であり、割り算は切り捨てです)。
また、逆にそういった真ん中から折り返したような複素配列をIDFTすると実数だけの結果が得られます。
FFTWのサンプルの多くは通常のDFT/IDFTを紹介しており、入出力のバッファには複素配列を与えています。が、私は今回、DFTは実数を入力とし、IDFTはエルミートな複素配列を入力とすることを前提としていたので先のプランを用意したわけです。
でそこまではよかったのですが、うっかり複素配列もサイズNで確保しちゃってたんですよね。別にバグは起きませんし、用意されていたとしても使われないのですが、せっかくメモリを節約できるのに勿体ないですよね。マニュアルを読み直して良かったよほんと。
ただ、そこで複素配列のサイズをN/2+1に変更したため、DFT後に一時バッファから出力バッファへコピーしたり、あるいはIDFTの前に入力バッファを一時バッファにコピーしたりという作業でコピーする領域のサイズをNのままにしており、ここでも見事メモリ領域を破壊してました;;
まったく、焦ってコトはやるもんじゃないですね・・・。
とまぁそんなこんなでツマラナイミスに苛まれつつ、なんとか動きかけのところまで持ってきました。残すところもうあとわずか。ほんとだと8月中に片付けたかったんですが、間に合わず。デッドラインは9月末ですが、できるだけ早く片付けたいところ。この土日で片付けたいな。
ところでVC++のデバッガの使い方ってどうやったら勉強できるんかなぁ? もっと使いこなせたらきっと便利やと思うんやけど・・・。表面的な機能しか使えてないし、もうちょっとどうにかしたいところ。
特にnewで確保した配列をウォッチリストで手軽に監視する方法ないだろうか・・・? 確かにC/C++の仕様から言って配列とポインタにほぼ差がないから表示しにくいってのは分かるけど、何か方法ないもんかなぁ?
デバッガとにらめっこしてた、そんな今日1日でした(苦笑