8

Chciałbym wiedzieć, czy jakaś struktura zawiera więcej niż jeden prymityw, ale jego całkowity rozmiar jest mniejszy lub równy rozmiarowi pojedynczego rejestru cpu, jak rejestr 4-bajtowy, czy kiedykolwiek ma sens kompilator do umieść go w jednym z tych 4-bajtowych rejestrów, przekazując go przez wartość lub odwołanie do funkcji, zamiast robić kopię na stosie wywoływanym lub przekazywać do niego wskaźnik i na ogół przekazując coś więcej niż pojedynczemu prymitywu do funkcjonować jak tablica lub struktura przechodząca w rejestrze cpu kiedykolwiek się przyda?Czy kiedykolwiek ma sens kompilator przekazać taką strukturę w rejestrze procesora do funkcji?

próbka takiej strukturze:

struct sample{ 
public: 
    char char1; 
    char char2; 
}; 

próbka przepuszczenie struktury do funkcji:

void someFunc(const sample input){ 
//whatever 
} 
void someFunc(sample input){ 
//whatever 
} 
void someFunc(sample & input){ 
//whatever 
} 
void someFunc(const sample & input){ 
//whatever 
} 
+5

Możesz zrzucić ten tasiemek zdania. Bardzo ciężko to trawię. – sbi

Odpowiedz

4

Tak. Wiele kompilatorów ma specjalny atrybut słowa kluczowego lub typu, którego można użyć do określenia, że ​​struktura powinna być przekazywana w rejestrach, a nie na stosie. Jest bardziej powszechny na procesorach, które mają wiele rejestrów i głębokich potoków, takich jak PowerPC, i może być ogromnym ulepszeniem wydajności w architekturach, w których zapis wartości do pamięci, a następnie ponowne jej odczytanie od razu powoduje przeciągnięcie potoku.

Zwykle używałbyś go tylko dla struktury, która ma taki sam rozmiar jak macierzysty rejestr. W szczególności jest to użyteczne w przypadku procesorów posiadających szerokie rejestry SIMD, które mogą przesyłać 16 bajtów naraz lub więcej. Pozwoliłoby to przejść (na przykład) 4-wymiarowym wektorem (cztery przepływy) na jednym rejestrze. AMD's System V jest przykładem ABI x86, który na to pozwala.

Innym przykładem jest atrybut typu d64_abi GCC, który mówi PowerPC, aby przekazał strukturę rejestrów, gdzie to możliwe, a nie na stosie. (Jest to część Darwin ABI).

typedef struct { 
    int   a; 
    float  f; 
    char   c; 
} __attribute__ ((d64_abi)) Thingy; 

Thingy foo(Thingy t); 

W powyższym przypadku, wywołanie Foo minie thingy na jednym rejestrze pływaka i dwóch int rejestrów, zamiast pisać go na stosie i czytając go z powrotem ponownie.Wartość zwracana wraca do rejestrów w ten sam sposób.

Nigdy nie widziałem kompilatora, który robi to automatycznie, bez twojego mówienia o tym, ale jest możliwe, że istnieje.

+0

@Crashworks_but umieszczenie dwóch znaków w jednym rejestrze int jest bezcelowe, prawda? – Pooria

+0

@Pooria: Wcale nie - może być znacznie szybszy niż wykonanie dwóch podróży w obie strony do stosu podczas wywołania funkcji. – Crashworks

+0

@Crashworks_I oznacza, ponieważ ostateczną intencją jest wykonanie operacji na każdym z tych dwóch bajtów w strukturze w moim pytaniu, powinny one znajdować się w oddzielnych rejestrach i jeśli znajdują się one w jednym rejestrze int, powinny być rozdzielone później, aby wykonać operacje na każdym z nich. – Pooria

3

na niektórych architekturach (jak i386, wiem, że to stare, ale to co Dorastałem z;) z pewnością ma sens przekazanie go do rejestru, ponieważ pchanie i wyskakiwanie ze stosu zabiera o wiele więcej (powiedzmy od 3 do 6 razy więcej) cykli procesora jako przechodzących przez rejestr. Zatem kompilator wykonałby dobrą robotę optymalizującą to.

Mogę sobie wyobrazić, że istnieją inne architektury, w których nie ma to znaczenia. Lub jeśli rejestry są używane do innych optymalizacji, które dają więcej ulepszeń, nie ma sensu ich używać do tego.

Jaką architekturę używasz/kierowania, czy też pytasz w ogóle?

+0

@littlegreen_In ogóle. – Pooria

5

Jest to zdefiniowane w interfejsie binarnym aplikacji (ABI) twojego środowiska wykonawczego. Standard nie mówi nic o rejestrach procesorów, gdy wywoływana jest funkcja, więc można utworzyć środowisko, w którym małe elementy są pakowane do pojedynczego rejestru procesora.

W przypadku części referencyjnej bardzo prawdopodobne jest, że zostaną one przekazane jako wskaźniki, ponieważ gdy wewnątrz wywoływanej funkcji zostanie podany adres odwołania, musi on zostać rozwiązany na adres przywoływanego obiektu.

1

Myślę, że istnieją kompilatory, które będą przekazywać POD w rejestrach, nawet jeśli są one struct s.

+0

Chciałbym wiedzieć, jakie one były! – Crashworks

+0

@Crashworks: Właściwie, oczekiwałbym, że zrobią to wszystkie kompilatory głównego nurtu (VC, GCC). – sbi

+0

Nie tak bardzo, obawiam się. Na przykład MSVC zamienia http://codepad.org/oJoKVbJb w http://codepad.org/PV7vpM8O: przechodzi przez strukturę dwóch znaków, przesuwając każdy znak na stos indywidualnie, nawet przy "pełnej optymalizacji". Działa lepiej, gdy zarówno Foo(), jak i Bar() są statykami w tej samej jednostce kompilacji i mogą zignorować konwencję wywołującą. – Crashworks

Powiązane problemy