el mejor método multiplataforma para obtener memoria alineada


Aquí está el código que normalmente uso para alinear la memoria con Visual Studio y GCC

inline void* aligned_malloc(size_t size, size_t align) {
    void *result;
    #ifdef _MSC_VER 
    result = _aligned_malloc(size, align);
    #else 
     if(posix_memalign(&result, align, size)) result = 0;
    #endif
    return result;
}

inline void aligned_free(void *ptr) {
    #ifdef _MSC_VER 
        _aligned_free(ptr);
    #else 
      free(ptr);
    #endif

}

¿Este código está bien en general? También he visto a la gente usar _mm_malloc, _mm_free. En la mayoría de los casos que quiero memoria alineada es utilizar SSE/AVX. Puedo usar esas funciones en general? Haría mi código mucho más simple.

Por último, es fácil crear mi propia función para alinear la memoria (ver más abajo). ¿Por qué entonces hay tantas funciones comunes diferentes para obtener memoria alineada (muchas de que solo funcionan en una plataforma)?

Este código hace una alineación de 16 bytes.

float* array = (float*)malloc(SIZE*sizeof(float)+15);

// find the aligned position
// and use this pointer to read or write data into array
float* alignedArray = (float*)(((unsigned long)array + 15) & (~0x0F));

// dellocate memory original "array", NOT alignedArray
free(array);
array = alignedArray = 0;

Véase: http://www.songho.ca/misc/alignment/dataalign.html y ¿Cómo asignar memoria alineada solo usando la biblioteca estándar?

Editar: En caso de que a alguien le importe, tengo la idea de mi función aligned_malloc () de Eigen (Eigen/src/Core/util/Memory.h)

Editar: Acabo de descubrir que posix_memalign es indefinido para MinGW. Sin embargo, _mm_malloc funciona para Visual Studio 2012, GCC, MinGW y el compilador Intel C++, por lo que parece ser la solución más conveniente en general. También requiere usar su propia función _mm_free, aunque en algunas implementaciones puede pasar punteros desde _mm_malloc al estándar free / delete.

Author: Community, 2013-05-04

5 answers

La primera función que propongas funcionaría bien.

Su función "homebrew" también funciona, pero tiene el inconveniente de que si el valor ya está alineado, acaba de desperdiciar 15 bytes. Puede que a veces no importe, pero el sistema operativo puede ser capaz de proporcionar memoria que se asigna correctamente sin ningún desperdicio (y si necesita alinearse a 256 o 4096 bytes, corre el riesgo de perder mucha memoria agregando "alineación-1" bytes).

 4
Author: Mats Petersson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-04 17:43:11

Mientras estés de acuerdo con tener que llamar a una función especial para hacer la liberación, tu enfoque está bien. Sin embargo, haría sus #ifdefal revés: comience con las opciones especificadas por los estándares y vuelva a las opciones específicas de la plataforma. Por ejemplo

  1. Si __STDC_VERSION__ >= 201112L utilizar aligned_alloc.
  2. Si _POSIX_VERSION >= 200112L utilizar posix_memalign.
  3. Si _MSC_VER está definido, utilice el material de Windows.
  4. ...
  5. Si todo lo demás falla, simplemente use malloc/free y desactivar SSE / AVX codificar.

El problema es más difícil si desea poder pasar el puntero asignado a free; eso es válido en todas las interfaces estándar, pero no en Windows y no necesariamente con la función heredada memalign que tienen algunos sistemas unix.

 10
Author: R..,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-04 17:47:17

Aquí está una muestra fija de user2093113, el código directo no se compiló para mí (void* tamaño desconocido). También lo puse en una clase de plantilla overriding operator new/delete para que no tenga que hacer la asignación y la colocación de llamadas new.

#include <memory>

template<std::size_t Alignment>
class Aligned
{
public:
    void* operator new(std::size_t size)
    {
        std::size_t space = size + (Alignment - 1);
        void *ptr = malloc(space + sizeof(void*));
        void *original_ptr = ptr;

        char *ptr_bytes = static_cast<char*>(ptr);
        ptr_bytes += sizeof(void*);
        ptr = static_cast<void*>(ptr_bytes);

        ptr = std::align(Alignment, size, ptr, space);

        ptr_bytes = static_cast<char*>(ptr);
        ptr_bytes -= sizeof(void*);
        std::memcpy(ptr_bytes, &original_ptr, sizeof(void*));

        return ptr;
    }

    void operator delete(void* ptr)
    {
        char *ptr_bytes = static_cast<char*>(ptr);
        ptr_bytes -= sizeof(void*);

        void *original_ptr;
        std::memcpy(&original_ptr, ptr_bytes, sizeof(void*));

        std::free(original_ptr);
    }
};

Úsalo así:

class Camera : public Aligned<16>
{
};

Aún no probamos la multiplataforma de este código.

 2
Author: speps,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-04 10:01:05

Si el compilador lo soporta, C++11 agrega una función std::align para hacer la alineación del puntero en tiempo de ejecución. Puedes implementar tu propio malloc / free así (no probado):

template<std::size_t Align>
void *aligned_malloc(std::size_t size)
{
    std::size_t space = size + (Align - 1);
    void *ptr = malloc(space + sizeof(void*));
    void *original_ptr = ptr;

    char *ptr_bytes = static_cast<char*>(ptr);
    ptr_bytes += sizeof(void*);
    ptr = static_cast<void*>(ptr_bytes);

    ptr = std::align(Align, size, ptr, space);

    ptr_bytes = static_cast<void*>(ptr);
    ptr_bytes -= sizeof(void*);
    std::memcpy(ptr_bytes, original_ptr, sizeof(void*));

    return ptr;
}

void aligned_free(void* ptr)
{
    void *ptr_bytes = static_cast<void*>(ptr);
    ptr_bytes -= sizeof(void*);

    void *original_ptr;
    std::memcpy(&original_ptr, ptr_bytes, sizeof(void*));

    std::free(original_ptr);
}

Entonces no tiene que mantener el valor del puntero original para liberarlo. Si esto es 100% portátil No estoy seguro, pero espero que alguien me corrija si no!

 1
Author: user2093113,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-04 18:43:28

Aquí están mis 2 centavos:

temp = new unsigned char*[num];
AlignedBuffers = new unsigned char*[num];
for (int i = 0; i<num; i++)
{
    temp[i] = new  unsigned char[bufferSize +15];
    AlignedBuffers[i] = reinterpret_cast<unsigned char*>((reinterpret_cast<size_t>
                        (temp[i% num]) + 15) & ~15);// 16 bit alignment in preperation for SSE
}
 0
Author: Mikhail,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-11 21:25:00