VC++(Visual C++)で配列を使おうとしたとき思い浮かぶものとして、CArray、CAtlArray、vector、配列があります。これらの違いについて考察していきたいと思います。
宣言、確保
例えば128個の配列を確保するときそれぞれ次のようにコーディングすることができます。(この他にも方法はありますが)
1 2 3 4 5 6 7 8 9 10 |
CArray < int > a; a.SetSize( 128 ); CAtlArray < int > b; b.SetCount( 128 ); vector < int > c; c.resize( 128 ); int d[ 128 ]; |
この場合、単純な配列に比べ、テンプレートオブジェクトの場合は2行程かかってしまいます。また、それぞれのオブジェクトを使うにはヘッダーのインクルードが必要です。CArrayの場合ですと#include <afxtempl.h>、CAtlArrayの場合は#include <altcoll.h>、vectorの場合には#include <vector>となります。
スタックサイズ
それぞれの宣言でどのぐらいスタック領域を使用するのか調べてみます。それぞれのオブジェクト、変数のsizeofをとることで計算することが出来ます。上記128個の配列の例で値を出すと、次のようになります。
1 2 3 4 |
CArray: 20 CAtlArray: 16 vector: 20 int [128]: 512 |
CAtlArrayは、得に継承を行っていないクラスのためもっともスタックサイズが小さくなりました。int [128]の場合確保した領域がすべてスタックに取られていることがわかります。
最大領域
最大領域を探るテストを行ってみました。
単純配列
まず、単純配列の場合、大きなサイズを割り当てようとしたところコンパイル段階で次のようなエラーが出ました。
エラー 1 error C2148: 配列サイズの合計は 0x7fffffff バイトを超えることはできません。
また、1MB程を割り当てた場合(1024×256個)コンパイル時にエラーになりませんが、実行段階に
0x00411a67 でハンドルされていない例外が発生しました: 0xC00000FD: Stack overflow
スタックオーバーフローのエラーが発生しました。ただこれはコンパイル時のスタックサイズ指定を変えてあげることで確保できるようにはなるようです。プロジェクトのプロパティのリンカのシステムのスタックサイズの設定。MSDNによると、
既定のスタックのサイズは、1 MB です。
とのことなので、1MBを割り当てることが出来ないのも納得です。
CArray
また、CArrayでは約1.45GB(1024x1024x371個)を割り当てようとしたとき
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application’s support team for more information.
このようなエラーが発生し確保は出来ませんでした。これは、つんでいるメモリの量などによるのかもしれません。(Windows タスクマネージャの利用可能な物理メモリもほぼ同じ値だったので)また、CArrayの場合memsetを使って0初期化及びコンストラクタ起動をするということで、確保するときに若干時間がかかるようです。
CAtlArray
CAtlArrayの場合も同様なサイズで割り当てが出来なくなりました。ただ、CArrayと違ってどうやらエラー例外を発生しないために単に確保がされなかったというだけのことで終わるようです。IsEmptyなどで調べるとそれが簡単にわかります。(もちろん、SetCountの返り値がbool型なのでそこでも判定できます。)
なお確保には、callocを使っているようで、やはり0クリア及びコンストラクタ起動をしているようです。new+memsetとcallocに速度差があるのかどうかは今後調べたいと思います。
vector
こちらもほぼ同様なようです。これがヒープの限界サイズなんでしょうか。CArrayの時と同様にエラー例外が発生しエラー文が標準出力に吐き出されました。vectorの場合には、new演算子で確保しているようです。0初期化はforをまわして0埋めしているような気がします。
ピンバック: VC++ CArray、CAtlArray、vector、配列、array(C++/CLI)の速度 | 豆知識