c++ - El mejor método multiplataforma para alinear la memoria.
performance sse (5)
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
}
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
}
¿Está bien este código en general? También he visto a personas usar _mm_malloc
, _mm_free
. En la mayoría de los casos, quiero memoria alineada para usar 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 alinear la memoria (muchas de las cuales solo funcionan en una plataforma)?
Este código hace 16 bytes de alineación.
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;
Consulte: http://www.songho.ca/misc/alignment/dataalign.html y ¿Cómo asignar memoria alineada solo con la biblioteca estándar?
Edición: En caso de que a alguien le importe, se me ocurrió la idea de mi función align_malloc () de Eigen (Eigen / src / Core / util / Memory.h)
Edición: Acabo de descubrir que posix_memalign
no está definido 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 el uso de su propia función _mm_free
, aunque en algunas implementaciones puede pasar punteros de _mm_malloc
al estándar free
/ delete
.
Aquí hay una muestra del usuario 2093113, el código directo no se construyó para mí (void * unknown size). También lo puse en una clase de plantilla que reemplaza al operador new / delete para que no tenga que hacer la asignación y la ubicación de la llamada 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 se ha probado la compatibilidad entre plataformas de este código.
La primera función que propones funcionaría bien.
Su función "homebrew" también funciona, pero tiene el inconveniente de que si el valor ya está alineado, acaba de perder 15 bytes. Puede que no importe a veces, pero el sistema operativo puede proporcionar memoria que se asigne 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).
Si el compilador lo admite, C ++ 11 agrega una función std::align
para realizar la alineación del puntero en tiempo de ejecución. Podrías implementar tu propio malloc / free de esta manera (sin probar):
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 tienes que mantener el valor del puntero original alrededor para liberarlo. Si esto es 100% portátil, no estoy seguro, ¡pero espero que alguien me corrija si no!
Siempre que esté de acuerdo con tener que llamar a una función especial para realizar la liberación, su enfoque es correcto. Sin embargo, haría su #ifdef
al revés: comience con las opciones especificadas por los estándares y #ifdef
a las específicas de la plataforma. Por ejemplo
- Si
__STDC_VERSION__ >= 201112L
usaaligned_alloc
. - Si
_POSIX_VERSION >= 200112L
usaposix_memalign
. - Si se define
_MSC_VER
, usa las cosas de Windows. - ...
- Si todo lo demás falla, solo use
malloc
/free
y desactive el código SSE / AVX.
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 memalign
heredada que memalign
algunos sistemas similares a Unix.