代码之家  ›  专栏  ›  技术社区  ›  Puppy

STL中的额外分配和神奇的空间缩减-使用rvalue引用

  •  2
  • Puppy  · 技术社区  · 15 年前

    我用一个分配器替换了标准分配器,这个分配器将“呼叫总部”它消耗多少内存。现在我正在浏览我的一些代码,想知道为什么它会分配然后释放这么多条目。

    只是作为参考,我不想预先优化我的代码或任何东西,我主要是好奇,除了我肯定需要知道我的总大小是否关闭,因为我需要知道我的对象到底有多少用于C#GC。

    使用以下示例函数:

    void add_file(string filename, string source) {
        file_source_map.insert(std::pair<const string, string>(std::move(filename), std::move(source)));
    }
    

    它分配6次(48字节),然后释放4次(32字节)。因为这对是一个rvalue,我将字符串移到其中,所以映射肯定会分配一个新节点,并将rvalue对移到其中,而不会触发任何更多的分配,当然也不必取消任何分配。文件名和源参数也来自rvalues,应该移入,而不是复制。只需注意:分配程序也在跟踪字符串,它不是std::string,而是 std::basic_string<char, std::char_traits<char>, Allocator<char>> .

    仅供参考,我在MSVC上。

    这是我的分配器代码:

    template<typename T>
    class Allocator {
    public : 
        //    typedefs
    
        typedef T value_type;
        typedef value_type* pointer;
        typedef const value_type* const_pointer;
        typedef value_type& reference;
        typedef const value_type& const_reference;
        typedef std::size_t size_type;
        typedef std::ptrdiff_t difference_type;
    
    public : 
        //    convert an allocator<T> to allocator<U>
    
        template<typename U>
        struct rebind {
            typedef Allocator<U> other;
        };
    
    public : 
        Parser* parser;
        inline ~Allocator() {}
        inline Allocator(Allocator const& other) {
            parser = other.parser;
        }
        inline Allocator(Parser* ptr)
            : parser(ptr) {}
        template<typename U>
        inline Allocator(Allocator<U> const& other) {
            parser = other.parser;
        }
    
        //    address
    
        inline pointer address(reference r) { return &r; }
        inline const_pointer address(const_reference r) { return &r; }
    
        //    memory allocation
    
        inline pointer allocate(size_type cnt, 
            typename std::allocator<void>::const_pointer = 0) { 
                int newsize = cnt * sizeof (T);
                parser->size += newsize;
                std::cout << "Allocated " << newsize << "\n";
                return reinterpret_cast<pointer>(::operator new(newsize)); 
        }
        inline void deallocate(pointer p, size_type count) {
            size_type size = count * sizeof(T);
            ::operator delete(p); 
            parser->size -= size;
            std::cout << "Deallocated " << size << "\n";
        }
    
        //    size
    
        inline size_type max_size() const { 
            return std::numeric_limits<size_type>::max() / sizeof(T);
        }
    
        //    construction/destruction
    
        inline void construct(pointer p, const T& t) { new(p) T(t); }
        inline void destroy(pointer p) { p->~T(); }
    
        inline bool operator==(Allocator const& other) { return other.parser == parser; }
        inline bool operator!=(Allocator const& a) { return !operator==(a); }
    };
    

    当我通过包装器函数从C调用add#u file(如上所述)时,我可以在控制台上清楚地看到每个分配和解除分配及其适当的大小,即4个8的分配,其中一个80来自映射,另外两个8的分配,然后4个8的解除分配,它告诉我函数中有四个多余的字符串,因为它们都是rvalues,没有理由发生任何释放。

    1 回复  |  直到 15 年前
        1
  •  5
  •   icecrime    15 年前

    我在VS 2010中运行了您的代码,我相信您看到的分配只是Visual Studio STL调试工具,因为所有8字节的分配都是从 _String_val 施工单位:

    • 释放( _ITERATOR_DEBUG_LEVEL == 0 ),构造函数很简单
    • 调试中( _ITERATOR_DEBUG_LEVEL != 0 ),它分配一个 _Container_proxy (大小正好是8)通过分配器。

    如果我以释放模式运行代码,映射的节点分配将降到72字节,8字节的分配和释放将消失:字符串似乎已正确移动。