Дополнительные ассигнования и уменьшение магического пространства в STL - используя ссылки на rvalue - PullRequest
2 голосов
/ 26 ноября 2010

Я заменил стандартный распределитель на распределитель, который будет "звонить домой" о том, сколько памяти он потребляет. Сейчас я перебираю часть своего кода и задаюсь вопросом, какого черта он выделяет, а затем освобождает так много записей.

Просто для справки, я не пытаюсь предварительно оптимизировать свой код или что-то еще, я в основном любопытен, за исключением того, что мне определенно нужно знать, выключен ли мой общий размер, потому что мне нужно точно знать, сколько мой объект использует для C # GC.

Возьмите эту функцию образца:

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

Он выделяется шесть раз (48 байт), а затем освобождается четыре раза (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); }
};

Когда я вызываю add_file (опубликованный выше) из C # через функции-оболочки, я ясно вижу каждое выделение и освобождение и их соответствующие размеры на консоли, и это четыре выделения из 8, одно из 80, которое, как я знаю, происходит из карта, еще два распределения по 8, а затем четыре освобождения по 8, что говорит мне, что в функции есть четыре избыточные строки, потому что все они являются значениями и нет причин для каких-либо освобождений.

1 Ответ

5 голосов
/ 27 ноября 2010

Я запустил ваш код в VS 2010, и я считаю, что выделенные ресурсы являются только средствами отладки Visual Studio STL, так как все 8-байтовые выделения выдаются из конструктора _String_val:

  • В выпуске (_ITERATOR_DEBUG_LEVEL == 0) конструктор тривиален
  • При отладке (_ITERATOR_DEBUG_LEVEL != 0) он выделяет _Container_proxy (размер которого равен 8) через распределитель.

Если я запускаю ваш код в режиме выпуска, выделение узла для карты падает до 72 байтов, а выделение и освобождение 8 байтов исчезают: кажется, что строки перемещены правильно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...