2017-04-26 8 views
7

Jestem początkującym początkującym, przeczytałem artykuł o tym temacie (ponieważ używam maszyny kompatybilnej z AVX2).Jak użyć tego makra do sprawdzenia, czy pamięć jest wyrównana?

Teraz, przeczytałem w this pytanie, aby sprawdzić, czy wskaźnik jest wyrównany.

ja sprawdzając je za pomocą tego przykładu zabawek main.cpp:

#include <iostream> 
#include <immintrin.h> 

#define is_aligned(POINTER, BYTE_COUNT) \ 
    (((uintptr_t)(const void *)(POINTER)) % (BYTE_COUNT) == 0) 


int main() 
{ 
    float a[8]; 
    for(int i=0; i<8; i++){ 
    a[i]=i; 
    } 
    __m256 evens = _mm256_set_ps(2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0); 
    std::cout<<is_aligned(a, 16)<<" "<<is_aligned(&evens, 16)<<std::endl; 
    std::cout<<is_aligned(a, 32)<<" "<<is_aligned(&evens, 32)<<std::endl; 

} 

i skompilować go z icpc -std=c++11 -o main main.cpp.

Drukowanie otrzymaną mieszaninę:

1 1 
1 1 

Jednakże, jeśli dodać thhese 3 linie przed 4 drukuje:

for(int i=0; i<8; i++) 
    std::cout<<a[i]<<" "; 
std::cout<<std::endl; 

Wynika to:

0 1 2 3 4 5 6 7 
1 1 
0 1 

Szczególnie , Nie rozumiem tego ostatniego 0. Dlaczego różni się od ostatniego drukowania? czego mi brakuje?

Odpowiedz

6

Twoja is_aligned (która jest makro, a nie funkcja) określa, czy obiekt został wyrównany do określonej granicy. Nie określa wymagania wyrównania typu obiektu.

Kompilator będzie gwarantował tablicę zmiennoprzecinkową, która będzie wyrównana do co najmniej wymogu wyrównania elementu pływającego - który zwykle wynosi 4. 32 nie jest współczynnikiem 4, więc nie ma gwarancji, że tablica zostanie wyrównana do granicy 32 bajtów. Istnieje jednak wiele adresów pamięci, które są podzielne zarówno przez 4, jak i 32, więc możliwe jest, że adres pamięci na granicy 4-bajtowej znajduje się również przy granicy 32-bajtowej. Tak było w pierwszym teście, ale jak wyjaśniono, nie ma gwarancji, że tak się stanie. W twoim ostatnim teście dodałeś kilka zmiennych lokalnych, a tablica znalazła się w innej lokalizacji pamięci. Tak się złożyło, że inne miejsce w pamięci nie znajdowało się na granicy 32 bajtów.

Aby zażądać ściślejsze dopasowanie, które mogą być wymagane przez instrukcje SIMD, można użyć alignas specyfikator:

alignas(32) float a[8]; 
+0

Dzięki za odpowiedź. Tak więc, aby upewnić się, że poprawnie zrozumiałem: wyobraźmy sobie, że możemy reprezentować pamięć jako sąsiednie bloki, z których każdy ma 4 bajty (przestrzeń zajmowana przez zmienną 'float'). Kompilator gwarantuje, że tablica jest wyrównana do tych 4-bajtowych bloków, więc tablica zaczyna się na początku bloku 4-bajtowego. Ale nie ma gwarancji, że tablica zaczyna się na początku tego fragmentu 32-bajtowego fragmentu (8 z tych 4-bajtowych bloków), ale może się zdarzyć przez przypadek. Czy to jest poprawne? – cplusplusuberalles

+0

Jedno dodatkowe pytanie: załóżmy, że mam funkcję, która ma argument "float *" jako argument wejściowy. Nie wiemy, czy jest wyrównany, czy nie. Jak mogę go dopasować? PS: Daj mi znać, czy bardziej odpowiednie jest otworzenie nowego pytania .. – cplusplusuberalles

+0

@plplusplusuberalles 1. Prawidłowo 2. Możesz oczywiście ustawić wskaźnik na inny adres pamięci, który jest wyrównany, ale co byś zrobił z takim wskaźnikiem? Zastanów się analogiczne pytanie: "Mam funkcję, która ma argument wejściowy int. Nie wiemy, czy jest podzielny przez 2. Jak mogę podzielić podzielić przez 2?". Możesz dodać 1, jeśli jest nieparzysty, ale tak jak w przypadku wskaźnika: Jak to jest przydatne? – user2079303

Powiązane problemy