views:

313

answers:

3

I'm using a custom allocator to account for memory usage in several containers. Currently I use a static variable to account for the memory usage. How could I separate this account across several containers without having to rewrite the allocator to use different static variables?


static size_t allocated = 0;


   template <class T>
   class accounting_allocator {
     public:
       // type definitions
       typedef T        value_type;
       typedef T*       pointer;
       typedef const T* const_pointer;
       typedef T&       reference;
       typedef const T& const_reference;
       typedef std::size_t    size_type;
       typedef std::ptrdiff_t difference_type;
       //static size_t allocated;

       // rebind allocator to type U
       template <class U>
       struct rebind {
           typedef accounting_allocator<U> other;
       };

       // return address of values
       pointer address (reference value) const {
           return &value;
       }
       const_pointer address (const_reference value) const {
           return &value;
       }

       /* constructors and destructor
        * - nothing to do because the allocator has no state
        */
       accounting_allocator() throw() {
       }
       accounting_allocator(const accounting_allocator&) throw() {
       }
       template <class U>
         accounting_allocator (const accounting_allocator<U>&) throw() {
       }
       ~accounting_allocator() throw() {
       }

       // return maximum number of elements that can be allocated
       size_type max_size () const throw() {
        //  std::cout << "max_size()" << std::endl;
           return std::numeric_limits<std::size_t>::max() / sizeof(T);
       }

       // allocate but don't initialize num elements of type T
       pointer allocate (size_type num, const void* = 0) {
           // print message and allocate memory with global new
           //std::cerr << "allocate " << num << " element(s)" << " of size " << sizeof(T) << std::endl;
           pointer ret = (pointer)(::operator new(num*sizeof(T)));
           //std::cerr << " allocated at: " << (void*)ret << std::endl;
           allocated += num * sizeof(T);
            //std::cerr << "allocated: " << allocated/(1024*1024) << " MB" << endl;
           return ret;
       }

       // initialize elements of allocated storage p with value value
       void construct (pointer p, const T& value) {
           // initialize memory with placement new
           new((void*)p)T(value);
      }

       // destroy elements of initialized storage p
       void destroy (pointer p) {
           // destroy objects by calling their destructor
           p->~T();
       }

       // deallocate storage p of deleted elements
       void deallocate (pointer p, size_type num) {
           // print message and deallocate memory with global delete
#if 0
           std::cerr << "deallocate " << num << " element(s)"
                     << " of size " << sizeof(T)
                     << " at: " << (void*)p << std::endl;
#endif
           ::operator delete((void*)p);
           allocated -= num * sizeof(T);
       }
   };
  template<>
    class accounting_allocator<void>
    {
    public:
      typedef size_t      size_type;
      typedef ptrdiff_t   difference_type;
      typedef void*       pointer;
      typedef const void* const_pointer;
      typedef void        value_type;

      template<typename _Tp1>
        struct rebind
        { typedef allocator<_Tp1> other; };
    };


   // return that all specializations of this allocator are interchangeable
   template <class T1, class T2>
   bool operator== (const accounting_allocator<T1>&,
                    const accounting_allocator<T2>&) throw() {
       return true;
   }
   template <class T1, class T2>
   bool operator!= (const accounting_allocator<T1>&,
                    const accounting_allocator<T2>&) throw() {
       return false;
   }
A: 

Put the declaration of "static size_t allocated" into the class defininition. Every template instantation will have a separate counter shared among all objects of this template.

drhirsch
+3  A: 

If you mean that you want a separate counter for each container type, you could simply include the container type as a template parameter and uncomment static size_t allocated so it's a static member variable. This way, a separate counter variable will be generated for each type of container.

If you're saying you want a separate counter for each instance of a container, you need to make size_t allocated a non-static member variable. The problem is, you'll also need some kind of hook so you can access the allocation counter from outside each container. The STL allocator design makes it difficult to do this. Some STL containers have a constructor that lets you pass an instance of an allocator, but not all containers support this. On containers that support this, you can include a reference to some global map inside your allocator class, and then pass an instance of your allocator to the constructor of each container. Then, when you call accounting_allocator::allocate(), the allocator would record the number of bytes it has allocated in the global map. Still, I can't see how you could easily associate this information with a particular container instance, since the allocator object does not know which container it belongs to.

Honestly, if you're just collecting debug info, it's probably easier to just define a non static size_t allocated, and have accounting_allocator::allocate() simply output the stats to a file or to stdout. Alternatively, look into using a memory profiler tool for the platform you develop on.

Charles Salvia
A: 

See my code samples:

// uintptr_t represents an object address
// as a numerical value.
// you could use unsigned long insead if 
// sizeof(long) == sizeof(void*) on your system.
struct AllocCounter {
    static size_t *Register(uintptr_t uContainer)
    {
        // insert container address into map, and
        // return an associated allocation counter.
    }
    static bool Unregister(uintptr_t uContainer)
    {
        // remove container address and the 
        // associated allocation counter from the map
    }
    static void DebugCounter(void)
    {
        // statistic of all container objects.
    }
protected:
    static hash_map<uintptr_t, size_t> m_aCounter;
};

Furthermore, you could associate container or object class name etc with the allocation counter by enhancing above AllocCounter.

And a container example:

class Container 
{
public:
    Container(void) 
    {
        m_pAllocCounter = AllocCounter::Register((uintptr_t)this);
        ....
    }
    ~Container()
    {
        AllocCounter::Unregister((uintptr_t)this);
    }
    pointer ObjectAllocate(void)
    {
        pointer obj;
        *m_pAllocCounter += sizeof *obj;
        obj = new CObject;
        return obj;
    }
    void ObjectDealloc(pointer pObj)
    {
        *m_pAllocCounter -= sizeof *pObj;
        delete pObj;
    }
    ....

private:
    size_t *m_pAllocCounter;
    ....
};
EffoStaff Effo