Enfoque moderno para hacer std :: vector asignar memoria alineada

11

La siguiente pregunta está relacionada, sin embargo, las respuestas son antiguas, y el comentario del usuario Marc Glisse sugiere que hay nuevos enfoques desde C ++ 17 a este problema que podrían no discutirse adecuadamente.

Estoy tratando de que la memoria alineada funcione correctamente para SIMD, sin dejar de tener acceso a todos los datos.

En Intel, si creo un vector de tipo flotante __m256y reduzco mi tamaño en un factor de 8, me da memoria alineada.

P.ej std::vector<__m256> mvec_a((N*M)/8);

De una manera un poco hacky, puedo lanzar punteros a elementos vectoriales para flotar, lo que me permite acceder a valores de flotación individuales.

En cambio, preferiría tener uno std::vector<float>que esté correctamente alineado y, por lo tanto, se pueda cargar en __m256y otros tipos de SIMD sin segfaullar.

He estado buscando en alineado_alloc .

Esto me puede dar una matriz de estilo C que está correctamente alineada:

auto align_sz = static_cast<std::size_t> (32);
float* marr_a = (float*)aligned_alloc(align_sz, N*M*sizeof(float));

Sin embargo, no estoy seguro de cómo hacer esto std::vector<float>. Dar la std::vector<float>propiedad de marr_a no parece ser posible .

He visto algunas sugerencias de que debería escribir un asignador personalizado , pero esto parece mucho trabajo, y tal vez con C ++ moderno ¿hay una mejor manera?

Prunus Persica
fuente
1
sin segfaulting ... o sin posibles ralentizaciones de las divisiones de línea de caché cuando se usa _mm256_loadu_ps(&vec[i]). (Aunque tenga en cuenta que con las opciones de ajuste predeterminadas, GCC divide las cargas / tiendas de 256 bits no alineadas garantizadas en vmovups xmm / vinsertf128. Por lo tanto, es una ventaja usar _mm256_loadover loadusi le importa cómo se compila su código en GCC si alguien olvida uso -mtune=...u -march=opciones.)
Peter Cordes

Respuestas:

1

Todos los contenedores en la biblioteca estándar de C ++, incluidos los vectores, tienen un parámetro de plantilla opcional que especifica el asignador del contenedor , y en realidad no es mucho trabajo implementar el suyo:

class my_awesome_allocator {
};

std::vector<float, my_awesome_allocator> awesomely_allocated_vector;

Tendrá que escribir un poco de código que implemente su asignador, pero no sería mucho más código del que ya escribió. Si no necesita soporte anterior a C ++ 17, solo necesita implementar los métodos allocate () y deallocate () , eso es todo.

Sam Varshavchik
fuente
También necesitan especializarseallocator_traits
NathanOliver
1
Este podría ser un buen lugar para una respuesta canónica con un ejemplo que las personas pueden copiar / pegar para saltar a través de los molestos aros de C ++. (Los puntos de bonificación si hay una manera de permitir que std :: vector intente reasignarse en lugar de la versión habitual de C ++ braindead siempre alloc + copy). También, por supuesto, tenga en cuenta que esto vector<float, MAA>no es compatible con el tipo vector<float>(y no puede ser porque todo lo que haga .push_backen un plano std::vector<float>compilado sin este asignador podría hacer una nueva asignación y copiar en una memoria mínimamente alineada. Y nuevo / eliminar no es compatible con alineado_alloc / gratis)
Peter Cordes
1
No creo que haya ninguna garantía de que el puntero devuelto por el asignador se use directamente como la dirección base de la std::vectormatriz. Por ejemplo, podría imaginar una implementación de std::vectorusar solo un puntero a la memoria asignada que almacena el final / capacidad / asignador en la memoria antes del rango de valores. Eso podría frustrar fácilmente la alineación realizada por el asignador.
Dietmar Kühl
1
Excepto que lo std::vectorgarantiza. Para eso lo usa. Quizás debería revisar lo que el estándar C ++ especifica aquí.
Sam Varshavchik
1
> También necesitan especializarse allocator_traits. No, no lo hacen. Todo lo que se necesita es implementar un asignador compatible.
Andrey Semashev