C ++ STL с jemalloc - PullRequest
       34

C ++ STL с jemalloc

11 голосов
/ 01 марта 2012

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

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

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


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

Ответы [ 5 ]

8 голосов
/ 26 июня 2012

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

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

6 голосов
/ 02 марта 2012

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

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

g++ yourprogram.cpp -ljemalloc

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

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

3 голосов
/ 01 марта 2012

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

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

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

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

Я полагаю, что Chromium и Firefox заменили распределитель, но они разрабатывают некоторые грязные приемы и, вероятно, должны обновить свой подход по мере развития компилятора, компоновщика и среды выполнения.

1 голос
/ 14 апреля 2016

Сделай себе распределитель. Делай так:

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

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

1 голос
/ 01 марта 2012

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

...