私はカスタムプログラミング言語(連結、ソフトタイプ)のインタープリターを作成しています。そのために、中央のデータ型がありToken
ます。トークンは、スカラーのものまたはベクトルのもののいずれかである、多くの異なるタイプの1つにすることができます。メモリの量を最小限に抑えるために、union
最初に使用しましたが、その後はユニオンで古いデータ構造しか使用できなかったため、struct
すべてのフィールド(long asInteger;
、、boost::shared_ptr<std::string> asString
...)を使用してaを使用しました。もちろん、それはメモリ消費の観点からは悪い考えでしたが、それで仕事は終わりました。
データ型の長さはほぼ100バイトであったためToken
(たとえば、1,000,000個の整数の配列をほぼ100メガバイトにする)、元の定式化は非常に不十分でした。今日、実装を改良して、コピーセマンティクスを使用して各要素に必要なメモリを動的に割り当てるようにunion
しました。これにより、クラスで使用できる場合と同様の結果が得られました。
これは新しいクラス定義です:
class Token {
protected:
TokenType tokenType_;
template<class T>
inline void copyToken(void * src, void * dst)
{
*static_cast<T*>(dst) =
*static_cast<T*>(src);
};
template<class T>
inline void deleteValue()
{
delete static_cast<T*>(data);
};
void deleteData()
{
switch (tokenType_)
{
case T_INTEGER: deleteValue<long>(); break;
case T_BOOL: deleteValue<bool>(); break;
case T_FLOAT: deleteValue<double>(); break;
case T_STRING: deleteValue<boost::shared_ptr<std::string>>(); break;
case T_ARRAY: deleteValue<boost::shared_ptr<std::vector<Token>>>(); break;
case T_HANDLE: deleteValue<HandleData>(); break;
default: ;
}
}
void allocate(const TokenType tokenType)
{
switch (tokenType)
{
case T_INTEGER: data = new long; break;
case T_BOOL: data = new bool; break;
case T_FLOAT: data = new double; break;
case T_STRING: data = new boost::shared_ptr<std::string>; break;
case T_ARRAY: data = new boost::shared_ptr<std::vector<Token>>; break;
case T_HANDLE: data = new HandleData; break;
default: data = NULL;
}
};
void * data;
public:
void set_type(const TokenType tokenType)
{
deleteData();
tokenType_ = tokenType;
allocate(tokenType);
};
Token() : tokenType_ (T_EMPTY) { data = NULL; };
Token(const TokenType tokenType) : tokenType_ (tokenType)
{
allocate(tokenType);
};
Token(const Token& old_token)
{
tokenType_ = old_token.tokenType_;
allocate(old_token.tokenType_);
switch (old_token.tokenType_)
{
case T_INTEGER: copyToken<long>(old_token.data, data); break;
case T_BOOL: copyToken<bool>(old_token.data, data); break;
case T_FLOAT: copyToken<double>(old_token.data, data); break;
case T_STRING: copyToken<boost::shared_ptr<std::string>>(old_token.data, data); break;
case T_ARRAY: copyToken<boost::shared_ptr<std::vector<Token>>>(old_token.data, data); break;
case T_HANDLE: copyToken<HandleData>(old_token.data, data); break;
default: ;
}
};
template<class T>
T& retreive()
{
return *static_cast<T*>(data);
};
template<class T>
const T& retreive() const
{
return *static_cast<T*>(data);
};
void operator=(const Token &rhs)
{
fileName = rhs.fileName;
lineNum = rhs.lineNum;
set_type(rhs.tokenType_);
switch (rhs.tokenType_)
{
case T_INTEGER: copyToken<long>(rhs.data, data); break;
case T_BOOL: copyToken<bool>(rhs.data, data); break;
case T_FLOAT: copyToken<double>(rhs.data, data); break;
case T_STRING: copyToken<boost::shared_ptr<std::string>>(rhs.data, data); break;
case T_ARRAY: copyToken<boost::shared_ptr<std::vector<Token>>>(rhs.data, data); break;
case T_HANDLE: copyToken<HandleData>(rhs.data, data); break;
default: ;
}
};
~Token()
{
deleteData();
};
};
次に、でトークンを作成します
Token newToken(T_INTEGER);
newToken.retreive<long>() = 42;
これで、上記のコードは機能しますが、非常に遅くなります(unionを使用した以前の実装よりも200%遅くなります)。new
プロファイラーは、実行時間のほぼ半分がとに費やされていることを示していfree()
ます。配置構文を使用して、オブジェクトのchar data[50]
スペースがToken
該当する場合にスタックに割り当てられ、すべての場合に1回だけ割り当てられるようにしました。これにより、速度は大幅に向上しますが、元の構文ほど速くはなりません(約20%遅くなります)。
私の質問は次のとおりです。この頻繁な小さなオブジェクトの割り当てをよりスリムで迅速にするにはどうすればよいでしょうか。
これが通常まったく異なる方法で行われる場合は、その方法を教えてください。私は通常、堅牢で高速ですが、「スマート」(自動変換など)ではないバリアント型が必要です。これには、独自のフレームワークがあるためです。一般的には、必要なメモリを最小限に抑えながら、メモリ割り当てコストを最小限に抑えたいと思います。
助けてくれてありがとう!