私はC++でvector型のデータを扱う時はいつも自作vectorクラスを使用しています。先日、Qtのvector型クラスであるQVectorを使ったときに自作クラスの処理と比較して、自作クラスの処理の遅さに愕然としました。
具体的には3Dモデルの読み込みを行わせたのですが、QVectorでは一瞬で読み込みが終わるのに対し、私のクラスだと嫌気がさすほど時間がかかります。そこで何がこんなに速度に差をもたらしているのか原因を探ってみることにしました。
結局、調べてわかった原因は私のクラスはvectorの大きさを変えるときに一度確保されていたメモリをすべて破棄してから、再度確保しなおして、古いデータをコピーするという方法で大きさを変更していたことでした。
100回Appendを実行すれば、メモリの解放と確保、そして全要素へのコピーが100回行われていたということになるので、考えてみれば遅くなるのは当然のことです。
しかし、ここでふと疑問が沸きました。new、deleteをメモリ操作に使うようにしている場合はreallocのようなメモリサイズを拡張するような処理はどうやって行えば良いのか。そもそも、私はいろいろな参考書などでC++ではmalloc, freeよりもnew, deleteを使うべきだと書いてあったのに影響されてnew, deleteを使うようにしていました。しかし、reallocを使わなければメモリ拡張は不可能なのでは?
結局、調べても無理やりreallocを使う方法やstd:vectorをメモリ拡張に使うなんていうあまりやりたくない方法くらいしかわからなかったので、最終的にnew, deleteをやめてmalloc, freeにすべて変えました。
これでメモリ領域の拡張ができるようになり、コピーの分の処理がなくなったのでずいぶん速度が向上しました。QVectorに比べるとまだやや遅いですが、以前のものに比べれば天と地の差です。
しかし、ここでまた問題が生じました。new, deleteをやめたことにより、コンストラクタ、デストラクタが呼べなくなってしまったため、動的メモリ確保を行うようなクラスを要素とするvectorを作る場合にうまく動作しなくなってしまったのです。
結局、そういったクラスを要素とするvectorにも対応させるためにはnew・deleteを使わざるを得ないという結論になり、再び悩みました。
new、delete
- コンストラクタ、デストラクタを呼べる
- 動的メモリ確保が必要な参照型のデータをメンバに持つようなデータ型を要素とするときにはどうしてもnew、deleteによるメモリ確保が必要
malloc、free
- reallocでメモリ拡張ができる
- charやintなど基本的な型を要素とする場合はこちらの方が適している?
最終的に基本型だけをmalloc、freeでメモリ管理し、その他の型はnew・deleteで行うという方向性で行くことに決めました。ちょっと無理やりですがtemplateを使ったら下記のような基本型かどうかを変別するinline関数は作れました。
template<typename T> inline bool IsFundamentalDataType(){
return typeid(T)==typeid(char)||typeid(T)==typeid(unsigned char)
|| typeid(T)==typeid(short)||typeid(T)==typeid(unsigned short)
|| typeid(T)==typeid(int)||typeid(T)==typeid(unsigned int)
|| typeid(T)==typeid(float)||typeid(T)==typeid(double)
|| typeid(T)==typeid(long double);
}
この関数を使って基本型かどうかを判別してmalloc・free、new・deleteを使い分けるメモリ管理関数を作りました。私の中ではこういう方向性でいくのがベストな方法という結論に至りましたが、まだまだ知識不足なのできっともっと良い方法があると思います。 人の書いたコードなどを見てもっと勉強する必要がありそうです。