URL: https://linuxfr.org/users/serge_ss_paille/journaux/alignement-chaotic-neutre Title: Alignement chaotic neutre Authors: serge_sans_paille Date: 2021年07月10日T23:58:41+02:00 License: CC By-SA Tags: c++ Score: 21 Demat' iNal, J'ai récemment eu l'ineffable [1] plaisir de corriger un bug dans LLVM qui m'a causé quelques mauvaises soirées. Afin que l'histoire devienne légende et que la légende fasse mythe, je me décide à vous en raconter quelques détails amusants. Considérons le bout de code suivant : ``` #include #include constexpr std::size_t align = 32; template using aligned_allocator = boost::alignment::aligned_allocator; using aligned_string = std::basic_string, aligned_allocator>; aligned_string make_string(int num) { return aligned_string(num, '0円'); } #include int main(int argc, char**argv) { auto s = make_string(argc); std::cout<< reinterpret_cast(s.data()) % align << std::endl; return 0; } ``` Rien de bien fantastique : on a besoin d'une chaîne alignée sur 32, on utilise un allocateur spécialisé pour ça, et tout va pour le mieux. Ce code produit le résultat escompté (afficher 0) avec gcc5, gcc6 etc. Mais, et c'est là que l'horreur commence, pas avec gcc 4.9, où il affiche *24*. Mais pourquoi donc? (pas pourquoi donc utiliser gcc 4.9, hein, même si la question se pose). Une première piste : sur une architecture 32 bit, il va revoyer 12 et pas 24... L'erreur du code présenté est de considérer que la pointeur renvoyé par `std::basic_string<...>::data()` est directement issu de l'allocateur mémoire. Rien n'oblige cela, et avec gcc 4.9, enfin avec la lib standard associée, l'organisation d'une string, c'est un header, puis le pointeur vers les données. Et le `aligned_allocator` est utilisé pour allouer toute la mémoire d'un coup, puis on fait quelques `reinterpret_cast` pour remplir le header correctement. La structure d'une string ressemble à ça [basic_string.h](https://github.com/gcc-mirror/gcc/blob/c8be0366b55cdb3233abcf79abe2c5ddc6f82cb9/libstdc%2B%2B-v3/include/bits/basic_string.h#L76) ``` [_Rep] _M_length [basic_string] _M_capacity _M_dataplus _M_refcount _M_p ----------------> unnamed array of char_type ``` l'allocation de l'objet complet: [basic_string.tcc](https://github.com/gcc-mirror/gcc/blob/c8be0366b55cdb3233abcf79abe2c5ddc6f82cb9/libstdc%2B%2B-v3/include/bits/basic_string.tcc#L607) ``` size_type __size = (__capacity + 1) * sizeof(_CharT) + sizeof(_Rep); /* ... */ void* __place = _Raw_bytes_alloc(__alloc).allocate(__size); _Rep *__p = new (__place) _Rep; ``` Suivant la taille du header (`sizeof(_Rep)` donc), on a un alignement qui diffère... enfer ! Avec l'arrivée de C++11, le copy-on-write n'est plus une optimisation acceptée par le standard (je crois) et l'organisation des string se simplifie, le comportement espéré réapparait. Je ne suis pas pour autant persuadé que l'on puisse reposer sur une quelconque garantie entre l'alignement de l'allocateur mémoire et l'alignement des données de notre string. Mais peut-être que la sapience collective saura m'éclaircir ? Conclusion de l'été : garder un string bien aligné, c'est compliqué. [1] pas tant que ça vu que je vous en narre les péripéties ici même

AltStyle によって変換されたページ (->オリジナル) /