Następujące pytanie związane jest jednak odpowiedzi są stare, i komentarz od użytkownika Marc Glisse sugeruje nowych podejść od C ++ 17 do tego problemu, które nie mogą być należycie rozpatrzone.
Próbuję uzyskać wyrównaną pamięć działającą poprawnie dla SIMD, wciąż mając dostęp do wszystkich danych.
W przypadku Intela, jeśli utworzę wektor zmiennoprzecinkowy typu __m256
i zmniejszy mój rozmiar 8-krotnie, otrzymam wyrównaną pamięć.
Na przykład std::vector<__m256> mvec_a((N*M)/8);
W nieco zhakowany sposób mogę rzutować wskaźniki na elementy wektorowe w celu unoszenia się na powierzchni, co pozwala mi na dostęp do poszczególnych wartości zmiennoprzecinkowych.
Zamiast tego wolałbym mieć std::vector<float>
poprawnie wyrównany, a zatem może być ładowany do __m256
innych typów SIMD bez segfault.
Patrzyłem na wyrównany_alloc .
To może dać mi tablicę w stylu C, która jest poprawnie wyrównana:
auto align_sz = static_cast<std::size_t> (32);
float* marr_a = (float*)aligned_alloc(align_sz, N*M*sizeof(float));
Nie jestem jednak pewien, jak to zrobić std::vector<float>
. Przekazanie std::vector<float>
własności marr_a
nie wydaje się możliwe .
Widziałem kilka sugestii, że powinienem napisać niestandardowy alokator , ale wydaje się, że to dużo pracy, a może w nowoczesnym C ++ jest lepszy sposób?
_mm256_loadu_ps(&vec[i])
. (Chociaż uwaga, że przy domyślnych opcjach tuningu, GCC dzieli nie gwarantowanych wyrównany 256-bitowe Obciążenia / sklepy w vmovups xmm / vinsertf128. Więc nie jest zaletą przy użyciu_mm256_load
ponadloadu
jeśli dbasz o to jak kompiluje kod na GCC, jeśli ktoś zapomni użycie-mtune=...
lub-march=
opcje.)Odpowiedzi:
Wszystkie kontenery w standardowej bibliotece C ++, w tym wektory, mają opcjonalny parametr szablonu, który określa alokator kontenera , a implementacja własnego nie jest naprawdę duża:
Będziesz musiał napisać trochę kodu, który implementuje twój alokator, ale nie byłby to dużo więcej kodu niż już napisałeś. Jeśli nie potrzebujesz obsługi wersji wcześniejszej niż C ++ 17, musisz jedynie zaimplementować metody replaceate () i deallocate () , to wszystko.
źródło
allocator_traits
vector<float, MAA>
nie jest to zgodne z typemvector<float>
(i nie może być, ponieważ cokolwiek, co działa.push_back
na zwykłymstd::vector<float>
kompilowanym bez tego alokatora, może dokonać nowego przydziału i skopiować go do minimalnie wyrównanej pamięci. A nowe / usuń nie jest zgodne z wyrównanym_alokiem / wolne)std::vector
tablicy. Na przykład mogę sobie wyobrazić implementacjęstd::vector
użycia tylko jednego wskaźnika do przydzielonej pamięci, która przechowuje koniec / pojemność / alokator w pamięci przed zakresem wartości. To z łatwością może udaremnić wyrównanie wykonane przez alokatora.std::vector
to gwarantuje. Do tego używa. Być może powinieneś przejrzeć to, co określa tutaj standard C ++.allocator_traits
- Nie, nie robią tego. Wystarczy zaimplementować zgodny alokator.