C++ STL с jemalloc



Как можно использовать контейнеры STL C++ с jemalloc (или любой другой реализацией malloc)?



Так ли это просто, как включить jemalloc/jemalloc.h? Или я должен написать распределитель для них?



Edit : приложение, над которым я работаю, выделяет и освобождает относительно небольшие объекты в течение всего срока службы. Я хочу заменить распределитель по умолчанию, потому что тесты показали, что приложение не масштабируется дальше 2 ядер. Профилирование показало, что он ждал выделения памяти, вот и все. что вызвало проблемы с масштабированием. Как я понимаю, jemalloc поможет в этом.





Я хотел бы видеть решение, которое не зависит от платформы, поскольку приложение должно работать как на Linux, так и на Windows. (Связывание с другой реализацией легко под Linux, но это очень трудно для Windows, насколько я знаю.)

738   5  

5 ответов:

C++ позволяет заменить operator new. Если эта замена operator new вызывает je_malloc, то std::allocator будет косвенно вызывать je_malloc, и в свою очередь все стандартные контейнеры будут.

Это, безусловно, самый простой подход. Написание пользовательского распределителя требует написания целого класса. Замена malloc может быть недостаточной (нет никакой гарантии, что не замененные operator new вызовы malloc), и это имеет риски, отмеченные ранее Адрианом Маккарти

Если вы хотите заменить malloc везде в вашей программе (что я хотел и также кажется единственным логическим решением), то все, что вам нужно сделать, это связать против него.

Итак, если вы используете gcc , то все, что вам нужно сделать, это:

g++ yourprogram.cpp -ljemalloc

Но, если это невозможно, то вы должны использовать jemalloc через другие функции, например je_malloc и je_free, а затем вы должны перегрузить операторы new и delete.

Нет необходимости включать какой-либо заголовок, Если вы не используете особенности реализации (в основном статистика).

Написание распределителя будет самым простым решением, так как stl был разработан, чтобы иметь взаимозаменяемые распределители. Это будет самый легкий путь.

Некоторые проекты играют в игры, пытаясь получить альтернативную реализацию malloc, чтобы заменить malloc и new, предоставляемые сопутствующей библиотекой компилятора. Это может привести к разного рода проблемам, потому что в конечном итоге вы полагаетесь на конкретные детали реализации вашего компилятора и библиотеки, которую он обычно использует. Этот путь чреват опасность.

Некоторые опасности попыток заменить malloc глобально:

  • порядок статического инициализатора имеет ограниченные гарантии в C++. Невозможно гарантировать, что замена распределителя будет инициализирована до того, как первый вызывающий объект попытается ее использовать, если только вы не запретите статические объекты, которые могут выделять память. Среда выполнения не имеет этой проблемы, так как компилятор и среда выполнения работают вместе, чтобы убедиться, что среда выполнения полностью инициализирована перед инициализацией любого статика.
  • Если вы динамически связываетесь с библиотекой времени выполнения, то нет никакого способа гарантировать, что часть кода библиотеки времени выполнения уже не привязана к ее собственной реализации. Попытка изменить библиотеку времени выполнения компилятора может привести к проблемам лицензирования при распространении приложения.
  • все другие методы распределения не всегда могут в конечном счете полагаться на malloc. Например, реализация new может обойти malloc для больших выделений и напрямую вызвать ОС к выделять память. Это требует отслеживания, чтобы убедиться, что такие распределения случайно не отправляются на замену free.
Я считаю, что Chromium и Firefox заменили распределитель, но они играют некоторые грязные трюки и, вероятно, должны обновить свой подход по мере развития компилятора, компоновщика и среды выполнения.

Могут возникнуть проблемы, так как конструкторы не будут вызваны. Вы можете использовать различные опции operator new (имеет больше опций, чем просто new), которые могут просто выделить память без вызова конструктора, или вызвать конструктор в уже выделенной памяти. http://www.cplusplus.com/reference/std/new/operator%20new%5B%5D/

Сделайте себя распределителем. Сделайте вот так:

#include <vector>

template<typename T>
struct RemoveConst
{
    typedef T value_type;
};

template<typename T>
struct RemoveConst<const T>
{
    typedef T value_type;
};

template <class T>
class YourAlloc {
public:
    // type definitions
    typedef RemoveConst<T>              Base;
    typedef typename Base::value_type   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;

    // rebind allocator to type U
    template <class U>
    struct rebind {
        typedef YourAlloc<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
    */
    YourAlloc() throw() {
    }
    YourAlloc(const YourAlloc&) throw() {
    }
    template <class U>
    YourAlloc(const YourAlloc<U>&) throw() {
    }
    ~YourAlloc() throw() {
    }

    // return maximum number of elements that can be allocated
    size_type max_size() const throw() {
        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) {
        return (pointer)je_malloc(num * sizeof(T));
    }

    // 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) {
        je_free(p);
    }
};

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

int main()
{
    std::vector<int, YourAlloc<int>> vector;

    return 0;
}

Код скопирован из здесь

Comments

    Ничего не найдено.