2012-03-01 2 views
10

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

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

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


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

ответ

6

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

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

1

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

3

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

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

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

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

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

+0

Я обновил мой вопрос, чтобы ответить вашим. Какие проблемы возникают при замене 'new'? – KovBal

+0

Если вы просто пытаетесь заменить 'new' обычными shenanigans C++, вы можете пройти. Это когда люди пытаются заменить «malloc» на всю программу, которая становится действительно волосатой. –

+0

Это именно то, что я хочу сделать: заменить 'malloc' на всю программу. Но я не хочу писать свою собственную реализацию; Я просто хочу использовать другую (хорошо протестированную). – KovBal

6

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

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

g++ yourprogram.cpp -ljemalloc 

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

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

1

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

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

код копируется из here

+0

Распределитель может быть хорошей идеей. Если большая часть данных хранится в контейнерах, поддерживающих распределитель, то это очень хорошее решение. – KovBal