C++でスタックでメモリ確保

動的配列を作るとき、普段でnewとかmallocとか使う。
それはヒープからメモリブロックを取得するのです。
小さいメモリブロックを取得する時、性能的にスタックから取得より低いと思う。

スタックからメモリブロックの取得は、allocaと言う関数がありますが、標準にその関数がありません。
じゃあその「標準」から離脱せずスタックからメモリブロックの取得する方法あるのかなって思って。ローカル変数を利用する方法を思いついた。

たとえ、この文字列のコードページを変更する関数


int EncodingConv(
const char* inpStr, int nInpLen,
char* outStr, int nOutLen,
const std::locale& srcLoc, const std::locale& dstLoc
) {
typedef std::codecvt<wchar_t, char, std::mbstate_t> MyCodeCvt;

std::vector<wchar_t> vbuf(nInpLen + 1);
wchar_t* buf = &vbuf[0];
int nBufLen = vbuf.size();

std::mbstate_t state = 0;
const char* inpMid;
const char* inpEnd = inpStr + nInpLen;
wchar_t* bufMid;
wchar_t* bufEnd = buf + nBufLen;
std::use_facet<MyCodeCvt>(srcLoc).in(state,
inpStr, inpEnd, inpMid,
buf, bufEnd, bufMid);

state = 0;
char* outMid;
char* outEnd = outStr + nOutLen - 1;
bufEnd = bufMid;
const wchar_t* cBufMid;
std::use_facet<MyCodeCvt>(dstLoc).out(state,
buf, bufEnd, cBufMid,
outStr, outEnd, outMid);
*outMid = 0;
return outMid - outStr;
}

その中にvectorを使う。vectorは動的メモリ確保の関数を使う。性能はこれで低くなるかも。この関数を改造して、スタックをその「buf」として使用すれば…

メモリ確保の部分は、こんな関数になった


template<int LEN>
inline int EncodingConvImpl(wchar_t* buf, int nBufLen,
const char* inpStr, int nInpLen,
char* outStr, int nOutLen,
const std::locale& srcLoc, const std::locale& dstLoc
) {
wchar_t localbuf[LEN];
return EncodingConvImpl<0>(localbuf, LEN, inpStr, nInpLen, outStr, nOutLen, srcLoc, dstLoc);
}
localbufはローカル変数ので、スタックで存在する。

そして元の関数は


template<>
inline int EncodingConvImpl<0>(wchar_t* buf, int nBufLen,
const char* inpStr, int nInpLen,
char* outStr, int nOutLen,
const std::locale& srcLoc, const std::locale& dstLoc
)
こうなる。これでスタックのメモリを使うようになった。

メモリブロックのサイズよって、テンプレートのパラメータは変わる


typedef std::codecvt<wchar_t, char, std::mbstate_t> MyCodeCvt;

if (buf == 0) {
if (nBufLen > 8192) {
std::vector<wchar_t> buf(nInpLen + 1);
return EncodingConvImpl<0>(&buf[0],nInpLen, inpStr,nInpLen, outStr,nOutLen, srcLoc,dstLoc);
}
#define FORWARDIMPLCALL(x,y) if(nBufLen > x) return EncodingConvImpl<y>(0,y, inpStr,nInpLen, outStr,nOutLen, srcLoc,dstLoc)
FORWARDIMPLCALL(7168, 8192);
FORWARDIMPLCALL(6411, 7168);
FORWARDIMPLCALL(5120, 6144);
FORWARDIMPLCALL(4096, 5120);
FORWARDIMPLCALL(3072, 4096);
FORWARDIMPLCALL(2048, 3072);
FORWARDIMPLCALL(1024, 2048);
FORWARDIMPLCALL(0, 1024);
#undef FORWARDIMPLCALL
}
そして元のコード

else if (buf != 0) {
std::mbstate_t state = 0;
const char* inpMid;
const char* inpEnd = inpStr + nInpLen;
wchar_t* bufMid;
wchar_t* bufEnd = buf + nBufLen;
std::use_facet<MyCodeCvt>(srcLoc).in(state,
inpStr, inpEnd, inpMid,
buf, bufEnd, bufMid);

state = 0;
char* outMid;
char* outEnd = outStr + nOutLen;
bufEnd = bufMid;
const wchar_t* cBufMid;
std::use_facet<MyCodeCvt>(dstLoc).out(state,
buf, bufEnd, cBufMid,
outStr, outEnd, outMid);
*outMid = 0;
return outMid - outStr;
}
}
最後

int EncodingConv(const char* inpStr, int nInpLen, char* outStr, int nOutLen,
const std::locale& srcLoc, const std::locale& dstLoc)
{
return EncodingConvImpl<0>(0, nInpLen + 1, inpStr, nInpLen, outStr, nOutLen, srcLoc, dstLoc);
}

これでできた。