开发者

Bad allocator implementation

I've got a pretty simple problem. I've been whipping up a non-thread-safe allocator. The allocator is a fairly simple memory arena strategy- allocate a big chunk, put all allocations in it, do nothing for deallocation, remove the whole lot on arena destruction. However, actually attempting to use this scheme throws an access violation.

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) {}
};

I've initialized memory to point to a vector which is resized to the MaxMemorySize, and I've also initialized CurrentUsed to point to an int which is zero. I fed in an allocator with these values to the constructor of a std::unordered_map, but it keeps throwing an access violation in the STL internals. Any suggestions?

Edit: Here's my usage:

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
Q开发者_开发技巧ueryPerformanceCounter(&t1);

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

Edit: Bloody hell. It worked when I increased the size to 1MB. I had to increase it to over 800,000 bytes to get it to work without throwing.


When I test this code, the rebind is being used to request multiple allocators against the same memory block. I put

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

at the top of allocate(size_type) and when I added three elements to a unordered_map got:

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<...>

If your implementation isn't coincidentally using nice round 64-byte requests this class will return mis-aligned allocations.


MSVC10's hashtable types just has a ton of space overhead for small value types. It's overrunning the amount of space you've reserved and throwing bad_alloc.

It's implemented as a list<value_t> holding all the elements and a hash bucket vector<list<value_t>::iterator> with between 2 and 16 slots per element.

That's a total of 4 to 18 pointers of overhead per element.

Something like this implementation is probably required by the standard. Unlike vector, unordered_map has a requirement that elements not be moved once added to the container.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜