2012-03-01 14 views
10

¿Cómo es posible usar contenedores C++ STL con jemalloc (o cualquier otra implementación malloc)?C++ STL con jemalloc

¿Es tan simple como incluir jemalloc/jemalloc.h? ¿O debería escribir un asignador para ellos?

Editar: La aplicación en la que estoy trabajando asigna y libera objetos relativamente pequeños a lo largo de su vida útil. Quiero reemplazar el asignador predeterminado, porque los puntos de referencia mostraron que la aplicación no escala más allá de 2 núcleos. El perfil mostró que estaba esperando la asignación de memoria, eso es lo que causó los problemas de escala. Según tengo entendido, jemalloc me ayudará con eso.


Me gustaría ver una solución, que sea neutral en cuanto a la plataforma, ya que la aplicación debe funcionar tanto en Linux como en Windows. (La vinculación con una implementación diferente es fácil en Linux, pero es muy difícil en Windows, hasta donde yo sé).

Respuesta

6

C++ le permite reemplazaroperator new. Si este reemplazo operator new llama a je_malloc, entonces std::allocator llamará indirectamente a je_malloc, y a su vez todos los contenedores estándar lo harán.

Este es de lejos el enfoque más simple. Escribir un asignador personalizado requiere escribir una clase completa. Sustitución malloc puede no ser suficiente (no hay garantía de que las llamadas operator new no reemplazados malloc), y tiene los riesgos señalados anteriormente por Adrian McCarthy

3

Escribir un asignador va a ser la solución más fácil, ya que el stl fue diseñado para tener asignadores intercambiables. Este será el camino más fácil.

Algunos proyectos juegan juegos intente obtener la implementación alternativa malloc para reemplazar malloc y new proporcionados por la biblioteca complementaria del compilador. Eso es propenso a todo tipo de problemas porque terminas confiando en los detalles de implementación específicos de tu compilador y la biblioteca que normalmente usa. Este camino está lleno de peligro.

Algunos peligros de tratar de reemplazar malloc a nivel mundial:

  • fin inicializador estático tiene garantías limitadas en C++. No hay forma de garantizar que el reemplazo del asignador se inicialice antes de que el primer llamante intente usarlo, a menos que prohíba objetos estáticos que puedan asignar memoria. El tiempo de ejecución no tiene este problema, ya que el compilador y el tiempo de ejecución funcionan en conjunto para asegurarse de que el tiempo de ejecución esté completamente inicializado antes de inicializar cualquier estática.
  • Si realiza un enlace dinámico a la biblioteca de tiempo de ejecución, no hay manera de asegurarse de que parte del código de la biblioteca de tiempo de ejecución ya no esté vinculado a su propia implementación. Intentar modificar la biblioteca de tiempo de ejecución del compilador puede generar problemas de licencia al redistribuir su aplicación.
  • Todos los otros métodos de asignación pueden no siempre depender al final de malloc. Por ejemplo, una implementación de new puede omitir malloc para grandes asignaciones y llamar directamente al sistema operativo para asignar memoria. Esto requiere un seguimiento para asegurarse de que dichas asignaciones no se envíen accidentalmente al reemplazo free.

Creo que Chromium y Firefox han reemplazado el asignador, pero juegan algunos trucos sucios y probablemente tengan que actualizar su enfoque a medida que evolucionan el compilador, el enlazador y el tiempo de ejecución.

+0

He actualizado mi pregunta para responder a sus de. ¿Qué tipo de problemas hay con la sustitución de 'nuevo'? – KovBal

+0

Si solo está tratando de reemplazar 'new' con los habituales chanchullos de C++, puede hacerlo. Es cuando las personas intentan reemplazar 'malloc' en todo un programa que se pone realmente peludo. –

+0

Esto es exactamente lo que quiero hacer: reemplazar 'malloc' en todo el programa. Pero no quiero escribir mi propia implementación; Solo quiero usar otra (bien probada). – KovBal

6

Si desea reemplazar malloc en todas partes de su programa (lo que quería y también parece ser la única solución lógica), entonces todo lo que tiene que hacer es crear un vínculo.

Por lo tanto, si se utiliza gcc entonces todo lo que tiene que hacer es:

g++ yourprogram.cpp -ljemalloc 

Pero, si no es posible, entonces usted tiene que utilizar jemalloc a través de otro ejemplo funciones je_malloc y je_free, y luego tiene que sobrecargar los operadores new y delete.

No hay necesidad de incluir ningún encabezado si no utiliza funciones específicas de implementación (estadísticas, principalmente).

1

Hazte asignador. Hacer como esto:

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

El código es copiado de here

+0

Un asignador puede ser una buena idea. Si la mayoría de los datos se almacenan en contenedores con reconocimiento de asignación, entonces es una muy buena solución. – KovBal