Плохая реализация распределителя - PullRequest
1 голос
/ 11 августа 2011

У меня довольно простая проблема.Я подхватываю не потокобезопасный распределитель.Распределитель представляет собой довольно простую стратегию арены памяти - выделите большой кусок, поместите в него все выделения, ничего не делайте для освобождения, удалите весь участок на уничтожение арены.Однако, фактически пытаясь использовать , эта схема вызывает нарушение прав доступа.

static const int MaxMemorySize = 80000;
template <typename T>
class LocalAllocator
{
  public:
      std::vector<char>* memory;
      int* CurrentUsed;
      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::size_t difference_type;

    template <typename U> struct rebind { typedef LocalAllocator<U> other; };

    template <typename U>
    LocalAllocator(const LocalAllocator<U>& other) {
        CurrentUsed = other.CurrentUsed;
        memory = other.memory;
    }
    LocalAllocator(std::vector<char>* ptr, int* used) {
        CurrentUsed = used;
        memory = ptr;
    }
    template<typename U> LocalAllocator(LocalAllocator<U>&& other) {
        CurrentUsed = other.CurrentUsed;
        memory = other.memory;
    }
    pointer address(reference r) { return &r; }
    const_pointer address(const_reference s) { return &r; }
    size_type max_size() const { return MaxMemorySize; }
    void construct(pointer ptr, value_type&& t) { new (ptr) T(std::move(t)); }
    void construct(pointer ptr, const value_type & t) { new (ptr) T(t); }
    void destroy(pointer ptr) { static_cast<T*>(ptr)->~T(); }

    bool operator==(const LocalAllocator& other) const { return Memory == other.Memory; }
    bool operator!=(const LocalAllocator&) const { return false; }

    pointer allocate(size_type n) {
        if (*CurrentUsed + (n * sizeof(T)) > MaxMemorySize)
            throw std::bad_alloc();
        auto val = &(*memory)[*CurrentUsed];
        *CurrentUsed += (n * sizeof(T));
        return reinterpret_cast<pointer>(val);
    }
    pointer allocate(size_type n, pointer) {
        return allocate(n);
    }
    void deallocate(pointer ptr, size_type n) {}

    pointer allocate() {
        return allocate(sizeof(T));
    }
    void deallocate(pointer ptr) {}
};

Я инициализировал memory, чтобы указать на вектор, который изменен до MaxMemorySize, и я 'Мы также инициализировали CurrentUsed, чтобы он указывал на int, который равен нулю.Я добавил распределитель с этими значениями в конструктор std::unordered_map, но он продолжает вызывать нарушение доступа во внутренних органах STL.Любые предложения?

Редактировать: Вот мое использование:

std::vector<char> memory;
int CurrentUsed = 0;
memory.resize(80000);
std::unordered_map<int, int, std::hash<int>, std::equal_to<int>, LocalAllocator<std::pair<const int, int>>> dict(
    std::unordered_map<int, int>().bucket_count(),
    std::hash<int>(),
    std::equal_to<int>(),
    LocalAllocator<std::pair<const int, int>>(&memory, &CurrentUsed)
);
// start timer
QueryPerformanceCounter(&t1);

for (int i=0;i<10000;i++)
    dict[i]=i; // crash

Редактировать: Кровавый ад.Это сработало, когда я увеличил размер до 1МБ.Мне пришлось увеличить его до 800 000 байт, чтобы заставить его работать без бросков.

Ответы [ 2 ]

2 голосов
/ 11 августа 2011

Когда я тестирую этот код, rebind используется для запроса нескольких распределителей для одного и того же блока памяти.Я помещаю

cout << n << " " << sizeof(T) << " " << typeid(T).name() << endl;

в верхней части allocate (size_type), и когда я добавляю три элемента в unordered_map, получим:

1 64 struct std::_List_nod<...>
16 4 struct std::_List_iterator<...>
1 64 struct std::_List_nod<...>
1 64 struct std::_List_nod<...>
1 64 struct std::_List_nod<...>

Если ваша реализация случайно не использует niceВ круглых 64-байтовых запросах этот класс будет возвращать неправильно выровненные распределения.

0 голосов
/ 06 ноября 2011

Типы хеш-таблиц MSVC10 просто имеют массу пространства для небольших типов значений.Он занимает больше места, которое вы зарезервировали, и выбрасывает bad_alloc.

Он реализован в виде list<value_t>, содержащего все элементы, и хеш-корзины vector<list<value_t>::iterator> с 2-16 слотами на элемент.

Это в общей сложности от 4 до 18 указателей накладных расходов на элемент.

Нечто подобное этой реализации, вероятно, требуется стандартом.В отличие от вектора, unordered_map требует, чтобы элементы не перемещались после добавления в контейнер.

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