0

我正在为自定义编程语言(连接,软类型)制作解释器,为此我有一个中心数据类型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%)。

我的问题是:我将如何让这种频繁分配的小对象变得更精简和更快?

如果这通常以完全不同的方式完成,请告诉我该怎么做。我通常需要一种健壮且快速但不是“智能”(自动转换,...​​)的变体类型,因为我有自己的框架。一般来说,我想最小化内存分配成本,同时也最小化所需的内存。

谢谢你的帮助!

4

1 回答 1

0

我知道这已经有好几年了,看来你找到了答案,但你从未发布过,所以如果有人偶然发现这篇文章(就像我一样),我会解释一些解决方案。

联盟

是的,可以使用您最终避免使用的相同数据结构。这将大大减少数据量。假设您有这四种类型:boolintfloat和 a double。联合体的大小总是最大的,这不是最好的,但它比所有元素的大小要好。现在,您说“ ......我只能在联合中使用普通的旧数据结构...... ”这是真的,但它可以使用指向非泛型类型的指针。这可以解决您的大部分内存问题,但如果您使用指针,您将不得不使用newdelete因此您仍然会因为这些问题而运行得更慢。使用联合的另一个缺点是您必须跟踪正在使用的类型。

提升::变体

如果您可以访问 boost 库,那么我肯定会选择这个。我认为它节省的内存不如联合多,但它比保存所有值节省的内存要多得多。这也允许非泛型类型。此外,您不必跟踪正在使用的类型。您只需要跟踪编写类型的顺序即可。

我会说使用Boost::variant但可以使用联合。

于 2014-11-19T22:22:22.827 回答