C++でスタックでメモリ確保
動的配列を作るとき、普段でnewとかmallocとか使う。
それはヒープからメモリブロックを取得するのです。
小さいメモリブロックを取得する時、性能的にスタックから取得より低いと思う。
スタックからメモリブロックの取得は、allocaと言う関数がありますが、標準にその関数がありません。
じゃあその「標準」から離脱せずスタックからメモリブロックの取得する方法あるのかなって思って。ローカル変数を利用する方法を思いついた。
たとえ、この文字列のコードページを変更する関数
その中にvectorを使う。vectorは動的メモリ確保の関数を使う。性能はこれで低くなるかも。この関数を改造して、スタックをその「buf」として使用すれば…
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;
}
メモリ確保の部分は、こんな関数になった
localbufはローカル変数ので、スタックで存在する。
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);
}
そして元の関数は
こうなる。これでスタックのメモリを使うようになった。
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);
}
これでできた。