2012-02-20 4 views
5

Czy wiele zmiennych, które są wyrównane do granic słów, jest szybsze niż operacje nieobciążonego ładowania na procesorach x86/64 (Intel/AMD 64-bit)?Czy ładunki wyrównane do słowa są szybsze niż nieprzypisane obciążenia na procesorach x64?

Mój kolega twierdzi, że niewyrównane obciążenia są powolne i należy ich unikać. Cytuje dopełnianie pozycji do granic wyrazów w strukturach jako dowód, że nieprzypisane obciążenia są wolne. Przykład:

struct A { 
    char a; 
    uint64_t b; 
}; 

Struktura A ma zwykle rozmiar 16 bajtów.

Z drugiej strony, documentation of the Snappy compressor stwierdza, że ​​Snappy zakłada, że ​​"niewyrównane 32- i 64-bitowe ładunki i sklepy są tanie". Według kodu źródłowego jest to prawda w przypadku 32-bitowych i 64-bitowych procesorów Intela.

A więc: jaka jest prawda? Jeśli i o ile wolniejsze są wolniejsze ładunki? W jakich okolicznościach?

+0

Domyślna struktura upakowania wynosi 8, a więc A :: członkiem b jest w rzeczywistości wyrównane. Niewspółosiadani członkowie mogą przechodzić przez linię pamięci podręcznej i to zawsze jest drogie. –

Odpowiedz

3

Wyrównane ładunki są sklepy są szybsze, dwa fragmenty Intel Optimization Manual czysto zwrócić na to uwagę:

3,6 OPTYMALIZACJA PAMIĘCI dostępów

dane wyrównanie, zwracając uwagę na układ danych i wyrównanie stosu

...

Problemy z dostosowywaniem i przesyłaniem są jednymi z najczęstszych źródeł dużych opóźnień w procesie przetwarzania sors na podstawie mikroarchitektury Intel NetBurst.

I

3.6.4 Wyrównanie

Dostosowanie danych dotyczy wszystkich rodzajów zmiennych:

• dynamicznie przydzielane zmienne

• Członkowie struktury danych

• Globalny o r zmienne lokalne

• parametrów przekazywanych na stos

dane niewyrównane dostępu mogą ponieść znaczne kary wydajności. Jest to szczególnie prawdziwe w przypadku podziałów linii pamięci podręcznej.

Po tej części w 3.6.4, nie jest to miłe reguła dla twórców kompilatora:

Montaż/Compiler Kodowanie Zasada 45. (wpływ H, H ogólność) Dopasuj dane dotyczące naturalnej wielkości argumentu granice adresów. Jeśli dane będą dostępne za pomocą wektora ładowanie instrukcji i zapisywanie, wyrównaj dane na 16-bajtowych granicach.

, po której następuje lista reguł wyrównania i kolejny klejnot w 3.6.6

Użytkownik/zasada kodowania źródłowego 6. (wpływ H M powszechność) dane Pad strukturami definiowanymi w kodzie tak, że każdy element danych dostosowane do naturalnej granicy adresu argumentu wielkości.

Obie zasady są oznaczone jako wysokiej uderzenia, co oznacza że mogą znacznie zmienić wydajność, wraz z fragmentami, reszta sekcji 3.6 jest wypełniona innych powodów, aby naturalnie wyrównać dane. Jest to warte każdego dewelopera czas na przeczytanie tych instrukcji, choćby po to, aby zrozumieć sprzęt, nad którym pracuje.

+0

Jeśli możesz zagwarantować, że Twoje nieprzypisane ładowanie/sklep nie przekroczy granicy linii pamięci podręcznej, nie ma żadnych kar za nowoczesną inteligencję. (W nowoczesnych AMD, być może granica 32-bajtowa lub 16-bajtowa). Zwykle zdecydowanie najłatwiejszym sposobem uniknięcia podziałów w pamięci podręcznej jest naturalne wyrównanie, ale jeśli masz uporządkowaną 64-bajtową strukturę, to wyrównanie pól wewnątrz niej jest w porządku. –

5

Random Guy w Internecie znalazłem mówi, że dla 486 mówi, że wyrównany dostęp 32-bitowy trwa jeden cykl. Niewyrównany dostęp 32-bitowy, który obejmuje quady, ale znajduje się w tej samej linii pamięci podręcznej, wymaga czterech cykli. Niewyrównany etc, który obejmuje wiele linii pamięci podręcznej może wymagać od sześciu do dwunastu cykli.

Biorąc pod uwagę, że niezaalibrowany dostęp wymaga dostępu do wielu quadów pamięci, prawie z definicji, wcale mnie to nie dziwi. Wyobrażam sobie, że lepsza wydajność buforowania na nowoczesnych procesorach sprawia, że ​​koszt jest trochę mniej zły, ale wciąż należy tego unikać.

(Nawiasem mówiąc, jeśli Twój kod ma żadnych pretensji do przenoszenia ... IA32 i potomkowie są prawie wyłącznie nowoczesnych architekturach, które wspierają niewyrównany dostęp w ogóle. ARM, na przykład, można bardzo między rzuca wyjątek, emulacji dostęp do oprogramowania, lub po prostu ładowania błędną wartość, w zależności od systemu operacyjnego)

Aktualizacja: Oto ktoś, kto rzeczywiście poszedł i measured it. Na swoim sprzęcie uważa, że ​​niewyrównany dostęp jest o połowę szybszy niż wyrównany. Idź, spróbuj sam ...

+0

+1 dla źródeł i linku na końcu –

+0

Niektóre warianty ARM powodują wyjątek na niealarmowanych dostępach, ale inne rozłożą je na mniejsze części. Na Cortex M3 słowo (32) ładuje/zapisuje na granicy pół-słowa (16) zostanie rozłożone na dwie części pół-słowa; słowo load/store na granicy bajtów zostanie rozłożone na trzy: dostęp dwubajtowy i dostęp do słowa. Zauważ, że nie wszystkie instrukcje pozwalają na dostęp bezadresowy. – supercat

+1

Na najnowszym Intel x86 (Nehalem i nowszym), niepodpisane ładunki i magazyny mają tylko kary za przekroczenie linii pamięci podręcznej (lub, co gorsza, linii strony). Zobacz http://agner.org/optimize/ w przewodniku microarch ze szczegółami. Może warto dodać prolog do pętli, aby wykonać ustawienie bez wyrównania, dopóki nie dojdziesz do wyrównanego adresu, więc główna pętla działa na wyrównanych danych, jeśli przetwarzasz każdy bajt. –

1

Aby naprawić niewyrównany odczyt, procesor musi wykonać dwa wyrównane odczyty i naprawić wynik. Jest to wolniejsze niż konieczność jednego odczytu i żadnych poprawek.

Kod Snappy ma specjalne powody do wykorzystywania braku dostępu. Będzie działać na x86_64; nie będzie działać na architekturach, w których brak dostępu nie jest opcją i będzie działał powoli, gdy utrwalenie niealibrowanego dostępu jest wywołaniem systemowym lub podobną drogą. (W DEC Alpha był mechanizm w przybliżeniu równoważny wywołanemu przez system wywołanemu ustawianiu nierównomiernego dostępu i trzeba było go włączyć dla twojego programu.)

Korzystanie z dostępu bez uprawnień to świadoma decyzja, którą autorzy Snappy'ego zrobili . To nie sprawia, że ​​każdy może naśladować to. Twórcy kompilatorów byliby prześladowani za niską wydajność swojego kodu, jeśli używali go domyślnie, na przykład.

1

Niewyrównane ładunki/magazyny nigdy nie powinny być używane, ale przyczyną jest nie wydajność. Powodem jest to, że język C zabrania im (zarówno poprzez reguły wyrównania, jak i reguły aliasingu) i nie działają one w wielu systemach bez bardzo powolnego kodu emulacji - kodu, który może również złamać model pamięci C11 potrzebny do właściwego zachowania się wielowątkowy kod, chyba że jest wykonywany na poziomie czysto bajtowym.

Jeśli chodzi o x86 i x86_64, w przypadku większości operacji (z wyjątkiem niektórych instrukcji SSE) niedopasowane obciążenie i składowanie są dozwolone, ale nie oznacza to, że są one tak szybkie, jak poprawny dostęp. Oznacza to tylko, że procesor wykonuje dla ciebie emulację i robi to nieco bardziej wydajnie, niż możesz zrobić sam.Na przykład, pętla o rozmiarze memcpy, która wykonuje źle wyrównany rozmiar i rozmiar tekstu zapisanego w słowie, będzie umiarkowanie wolniejsza niż ten sam memcpy, zapewniając wyrównany dostęp, ale będzie również szybsza niż pisanie własnej pętli kopiowania bajt po bajcie.

+0

Załóżmy, że chcemy skopiować 64 KB danych, w których źródło i miejsce docelowe są ustawione inaczej. Co byś uważał za kompromisy między (1) kopiowaniem jako bajtami; (2) wyrównaj źródło lub cel i skopiuj jako longwords z jednym wyrównanym i jednym niewyrównanym wskaźnikiem; (3) wyrównaj źródło lub cel i manipuluj nim jako słowami, a drugą częścią jako bajty lub pół słowa; (4) manipuluj zarówno źródłami, jak i celami jako słowami, stosując zmianę bitów, jeśli jest to konieczne, aby połączyć źródło i miejsce docelowe. Pamiętaj, że to, co jest szybkie w dzisiejszych procesorach, może być wolne jutro. – supercat

+0

Jeśli nie jesteś tym, który wdraża system, użyłbym '' memcpy' systemu. Najprawdopodobniej używa najszybszej rzeczy, a co ważniejsze, nie musisz się martwić, że kompilator wykryje, że złamałeś zasady aliasingu i tym samym złamałeś kod. –

+0

@R: Uczciwy punkt dotyczący memcpy w przypadku, gdy będziemy po prostu kopiować dane. Co jeśli zrobimy coś trochę bardziej skomplikowanego, np. odpowiednik - zakładając bajty - 'while while (n--) * dest ++^= * srC++;' Jeśli oba mają identyczne wyrównanie, jasne użycie słów dla większości operacji powinno pozwolić na znaczne przyspieszenie, ale co byłoby najbardziej rozsądny wzór kodowania takiego czegoś? – supercat

0

Niewyrównany dostęp 32 i 64-bitowy NIE jest tani.

Zrobiłem testy, aby to sprawdzić. Moje wyniki na Core i5 M460 (64-bitowe) były następujące: najszybsza liczba całkowita miała 32-bitową szerokość. Wyrównanie 64-bitowe było nieco wolniejsze, ale prawie takie samo. Wyrównanie 16-bitowe i wyrównanie 8-bitowe były zauważalnie wolniejsze niż w przypadku wyrównania 32- i 64-bitowego. 16 bitów jest wolniejsze niż wyrównanie 8-bitowe. Zdecydowanie najwolniejszą formą dostępu był nierównomierny dostęp 32-bitowy, który był 3,5 razy wolniejszy niż wyrównany dostęp 32-bitowy (najszybszy z nich), a niewyrównany dostęp 32-bitowy był nawet o 40% wolniejszy niż niewydzielony dostęp 64-bitowy.

Wyniki: https://github.com/mkschreder/align-test/blob/master/results-i5-64bit.jpg?raw=true kod Źródło: https://github.com/mkschreder/align-test

Powiązane problemy