C++ 变量类型令牌的内存分配

C++ 变量类型令牌的内存分配,c++,memory-management,data-structures,variant,C++,Memory Management,Data Structures,Variant,我正在为一种定制编程语言制作一个连接式、软类型的解释器,为此我有一个中心数据类型令牌。令牌可以是许多不同类型中的一种,可以是标量令牌,也可以是向量令牌。为了最大限度地减少内存量,我首先使用了一个联合体,但随后我只能在联合体中使用普通的旧数据结构,因此我求助于一个所有字段都长为integer的struct;,boost::共享\u ptr关联。。。。从内存消耗的角度来看,这当然是个坏主意,但它完成了任务 由于令牌数据类型的长度几乎为100字节,例如,使1000000个整数的数组几乎为100兆字节,

我正在为一种定制编程语言制作一个连接式、软类型的解释器,为此我有一个中心数据类型令牌。令牌可以是许多不同类型中的一种,可以是标量令牌,也可以是向量令牌。为了最大限度地减少内存量,我首先使用了一个联合体,但随后我只能在联合体中使用普通的旧数据结构,因此我求助于一个所有字段都长为integer的struct;,boost::共享\u ptr关联。。。。从内存消耗的角度来看,这当然是个坏主意,但它完成了任务

由于令牌数据类型的长度几乎为100字节,例如,使1000000个整数的数组几乎为100兆字节,因此原始公式显示出严重不足。今天,我修改了实现,以使用复制语义动态分配每个元素所需的内存,这样,如果我可以在类中使用union,就可以得到类似于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%。一个分析器显示,几乎一半的执行时间都花在了新的和免费的上。我尝试在char数据[50]上使用placement新语法,以便在适用时在堆栈上分配令牌对象的空间,并且在所有情况下只分配一次,这样可以大大提高速度,但速度仍然不如原来的快约20%

我的问题是:我将如何使这种频繁分配的小对象更精简、更快

如果这通常是做一个完全不同的方式,请告诉我怎么做。我通常需要一个变体类型,是稳健和快速,但不是智能自动转换。。。因为我有自己的框架。一般来说,我希望最小化内存分配成本,同时最小化所需的内存


谢谢你的帮助

我知道这已经有年历史了,你似乎找到了答案,但你从未发布过,所以如果有人像我一样偶然发现这篇文章,我将解释一些解决方案

协会 是的,可以使用您最终避免使用的相同数据结构。这将大大减少数据量。假设您有四种类型:bool、int、float和double。联合体的大小总是最大的,这不是最好的,但它比所有元素的大小都好。现在,你说…我只能在联盟中使用普通的旧数据结构。。。这是正确的,但它可以使用指向非泛型类型的指针。这可以解决大部分内存问题,但如果使用指针,则必须使用new和delete,因此运行速度仍然会变慢。使用联合的另一个缺点是,您必须跟踪正在使用的类型

Boost::variant 如果您可以访问boost库,那么我肯定会这么做。我认为它不像一个联合体那样节省内存,但它比保存所有的值节省了很多。这也允许非泛型类型。此外,您不必跟踪正在使用的类型。你所要做的就是记录你写类型的顺序


我会说使用Boost::variant,但使用union也是可能的。

为什么不直接使用Boost:variant呢?另外,C++11放宽了union的规则,重点是,我不想使用union,因为内存消耗非常大。至于Booost::变量,我不知道,会看看它。如果Booost:变量类不适合你,我会考虑使用一些继承来避免切换类型。实际上,Boo::变体看起来只是我正在寻找的解决方案!我不知道上次检查时为什么忽略了它。你可以把它作为答案贴出来,这样我就可以接受了。