简体   繁体   English

帐户 memory 使用自定义分配器

[英]Account memory usage with custom allocator

I'm using a custom allocator to account for memory usage in several containers.我正在使用自定义分配器来计算多个容器中的 memory 使用情况。 Currently I use a static variable to account for the memory usage.目前我使用 static 变量来说明 memory 的使用情况。 How could I separate this account across several containers without having to rewrite the allocator to use different static variables?我如何才能将此帐户跨多个容器分开,而不必重写分配器以使用不同的 static 变量?


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

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. 如果您希望为每个容器类型设置一个单独的计数器,则可以简单地将容器类型包括为模板参数,并取消注释static size_t allocated这样它就是静态成员变量。 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. 如果您说要为容器的每个实例设置单独的计数器,则需要为size_t allocated一个非静态成员变量。 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. STL分配器设计使得这很难做到。 Some STL containers have a constructor that lets you pass an instance of an allocator, but not all containers support this. 一些STL容器有一个构造函数,允许您传递分配器的实例,但并非所有容器都支持此。 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. 在支持此功能的容器上,您可以在allocator类中包含对某些全局映射的引用,然后将分配器的实例传递给每个容器的构造函数。 Then, when you call accounting_allocator::allocate() , the allocator would record the number of bytes it has allocated in the global map. 然后,当您调用accounting_allocator::allocate() ,分配器将记录它在全局映射中分配的字节数。 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. 但是,我无法看到如何轻松地将此信息与特定容器实例相关联,因为allocator对象不知道它属于哪个容器。

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. 老实说,如果你只是收集调试信息,可能更容易定义一个非静态size_t allocated ,并让accounting_allocator::allocate()只是将统计信息输出到文件或stdout。 Alternatively, look into using a memory profiler tool for the platform you develop on. 或者,考虑使用内存分析器工具为您开发的平台。

Put the declaration of "static size_t allocated" into the class defininition. 将“static size_t allocated”的声明放入类定义中。 Every template instantation will have a separate counter shared among all objects of this template. 每个模板即时将在此模板的所有对象之间共享一个单独的计数器。

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. 此外,您可以通过增强以上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;
    ....
};

This is an old question, but anyway, here's a solution for your issue.这是一个老问题,但无论如何,这是您问题的解决方案。 The idea is to capture statistics based on the type of the allocator.这个想法是根据分配器的类型捕获统计信息。

So we create a registry that's mapping the allocator's type to a statistic collecting structure like this:所以我们创建一个注册表,将分配器的类型映射到统计收集结构,如下所示:

struct registry
{
    struct alloc_track
    {
        size_t count;
        size_t max;
        size_t current;

        alloc_track() : count(0), max(0), current(0) {}
    };

    // Not using a STL container here for the key to avoid allocation */
    std::unordered_map<const char *, alloc_track> registry;

    static registry & get_instance() { static registry a; return a; }

    void credit(const char * key, std::size_t count, std::size_t size) {
        alloc_track & track = registry[key];
        track.count += count;
        track.max += size;
        track.current += size;
    }
    void debit(const char * key, std::size_t count, std::size_t size) {
        alloc_track & track = registry[key];
        track.count -= count;
        track.current -= size;
    }
    void dump() {
        // Not using C++ iostream here to avoid triggering the allocator itself
        printf("Allocator registry:\n");
        for (auto it : registry) {
            printf("%s: %lu instances %lu bytes, max usage %lu\n", it.first, it.second.count, it.second.current, it.second.max);
        }
    }

    ~registry() {
        dump();
    }
};

Then you'll plug the registry in your allocator this way:然后你将以这种方式将注册表插入你的分配器:

template <class T>
class accounting_allocator {
[...]
    // allocate but don't initialize num elements of type T
    pointer allocate (size_type num, const void* = 0) {
        pointer ret = (pointer)(::operator new(num*sizeof(T)));
        registry::get_instance().credit(typeid(T).name(), num, num * sizeof(T));
        return ret;
    }


    // deallocate storage p of deleted elements
    void deallocate (pointer p, size_type num) {
        // print message and deallocate memory with global delete
        ::operator delete((void*)p);
        registry::get_instance().debit(typeid(T).name(), num, num * sizeof(T));
    }

Any time you want to get the current allocation statistics, you can call:任何时候你想获得当前的分配统计信息,你可以调用:

registry::get_instance().dump();

Complete code can be found here完整的代码可以在这里找到

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM