С++ STL с jemalloc

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

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

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


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

Ответ 1

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

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

Ответ 2

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

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

g++ yourprogram.cpp -ljemalloc

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

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

Ответ 3

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

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

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

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

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

Ответ 4

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

Ответ 5

Сделайте себе распределитель. Сделайте следующее:

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

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