12.1.4.1 : Un allocateur de données alignées
Nous pourions utiliser des bibliothèques tièrces ou des memalign et autre posix_memalign pour aligner nos données, mais nous pouvons jouer le jeu jusqu'au bout en changeant l'allocateur des std::vector pour qu'ils aient eux-même des données alignées noteOn rappel au passage qu'avoir des donneés alignées évite que compilateur d'ajouter un peal avant la boucle de calculs vectorisés.
1 2 |
#ifndef __ALIGNED_ALLOCATOR_H__ #define __ALIGNED_ALLOCATOR_H__ |
Les includes classiques pour allouer des trucs :
1 2 3 4 5 6 |
#include <new> #include <limits> #include <iostream> #include <vector> #include <malloc.h> |
Notre allocateur :
1 2 3 4 5 6 7 8 9 10 11 |
///@brief Allocator wihch alignes data template<typename T> struct AlignedAllocator{ ///Value of the type of the current allocator (used by std::vector to avoid ambiguities if we use multiple template) typedef T value_type; AlignedAllocator () = default; //Declare the current constructor as default constructor ///Copy constructor from U type template<typename U> constexpr AlignedAllocator (const AlignedAllocator <U>&) noexcept {} |
La fonction d'allocation noteQui n'est malheureusement que unidimensionnelle, donc pas de padding implicite :
1 2 3 4 5 6 7 8 9 10 |
///Do the memory allocation /** @param n : number of element to be allocated * @return pointer to the allocated memory * This function is nodiscard, so return value cannot be ignored * We have to remove nodiscard when we make it static (and it works with std::vector) */ static /*[[nodiscard]]*/ T* allocate(std::size_t n){ if(n > std::numeric_limits<std::size_t>::max() / sizeof(T)){ throw std::bad_array_new_length(); } |
Ici, on utilise la macro VECTOR_ALIGNEMENT que l'on a définit dans le CMakeLists.txt, ce qui est moche, mais ce n'est qu'un exemple :
1 2 3 4 |
T* p = static_cast<T*>(std::aligned_alloc(VECTOR_ALIGNEMENT, n * sizeof(T))); if(p != NULL){ return p; } |
Si l'allocation se passe mal, on râle :
1 2 |
throw std::bad_alloc(); } |
La fonction de déallocation appelle juste std::free :
1 2 3 4 5 6 7 |
///Free the allocated memory /** @param p : pointer to be freed * @param n : number of element allocated in p */ static void deallocate(T* p, std::size_t n) noexcept{ std::free(p); } |
On termine la déclaration et le fichier :
1 2 3 4 |
};
#endif
|
Le fichier AlignedAllocator.h complet :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
/*************************************** Auteur : Pierre Aubert Mail : pierre.aubert@lapp.in2p3.fr Licence : CeCILL-C ****************************************/ #ifndef __ALIGNED_ALLOCATOR_H__ #define __ALIGNED_ALLOCATOR_H__ #include <new> #include <limits> #include <iostream> #include <vector> #include <malloc.h> ///@brief Allocator wihch alignes data template<typename T> struct AlignedAllocator{ ///Value of the type of the current allocator (used by std::vector to avoid ambiguities if we use multiple template) typedef T value_type; AlignedAllocator () = default; //Declare the current constructor as default constructor ///Copy constructor from U type template<typename U> constexpr AlignedAllocator (const AlignedAllocator <U>&) noexcept {} ///Do the memory allocation /** @param n : number of element to be allocated * @return pointer to the allocated memory * This function is nodiscard, so return value cannot be ignored * We have to remove nodiscard when we make it static (and it works with std::vector) */ static /*[[nodiscard]]*/ T* allocate(std::size_t n){ if(n > std::numeric_limits<std::size_t>::max() / sizeof(T)){ throw std::bad_array_new_length(); } T* p = static_cast<T*>(std::aligned_alloc(VECTOR_ALIGNEMENT, n * sizeof(T))); if(p != NULL){ return p; } throw std::bad_alloc(); } ///Free the allocated memory /** @param p : pointer to be freed * @param n : number of element allocated in p */ static void deallocate(T* p, std::size_t n) noexcept{ std::free(p); } }; #endif |