我正在为自定义编程语言(连接,软类型)制作解释器,为此我有一个中心数据类型Token
。令牌可以是许多不同类型中的一种,可以是标量类型,也可以是向量类型。为了最小化内存量,我使用了union
第一个,但后来我只能在联合中使用普通的旧数据结构,所以我使用了struct
所有字段(long asInteger;
、、boost::shared_ptr<std::string> asString
...)的 a。从内存消耗的角度来看,这当然是一个坏主意,但它完成了工作。
由于Token
数据类型的长度几乎为 100 字节(例如,使 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()
。我尝试在 a 上使用放置新语法,char data[50]
以便Token
在适用时在堆栈上分配对象的空间,并且在所有情况下仅分配一次,这可以大大加快速度,但仍不如原始语法快(慢约 20%)。
我的问题是:我将如何让这种频繁分配的小对象变得更精简和更快?
如果这通常以完全不同的方式完成,请告诉我该怎么做。我通常需要一种健壮且快速但不是“智能”(自动转换,...)的变体类型,因为我有自己的框架。一般来说,我想最小化内存分配成本,同时也最小化所需的内存。
谢谢你的帮助!