Tak, zarówno wyrównanie, jak i uporządkowanie danych może mieć dużą różnicę w wydajności, nie tylko o kilka procent, ale od kilku do wielu setek procent.
Weź tę pętlę, dwie instrukcje mają znaczenie, jeśli uruchomisz wystarczającą liczbę pętli.
.globl ASMDELAY
ASMDELAY:
subs r0,r0,#1
bne ASMDELAY
bx lr
Z pamięcią podręczną i bez niej oraz z wyrównywaniem z rzutem pamięci podręcznej i bez niego w przewidywaniu gałęzi i można znacznie różnić wydajność tych dwóch instrukcji (tyknięcia zegara):
min max difference
00016DDE 003E025D 003C947F
Test wydajności, który możesz bardzo łatwo zrobić sam. dodaj lub usuń kropki wokół testowanego kodu i wykonaj dokładną synchronizację, przenieś testowane instrukcje wzdłuż odpowiednio szerokiego zakresu adresów, aby dotknąć krawędzi linii pamięci podręcznej itp.
To samo dotyczy dostępu do danych. Niektóre architektury narzekają na niezrównany dostęp (na przykład wykonanie 32-bitowego odczytu pod adresem 0x1001), powodując błąd danych. Niektóre z nich można wyłączyć i przejąć wydajność. Inne, które umożliwiają dostęp bez wyrównania, po prostu dostają wydajność.
Czasami są to „instrukcje”, ale przez większość czasu są to cykle zegara / autobusu.
Spójrz na implementacje memcpy w gcc dla różnych celów. Powiedzmy, że kopiujesz strukturę 0x43 bajtów, możesz znaleźć implementację, która kopiuje jeden bajt, pozostawiając 0x42, a następnie kopiuje 0x40 bajtów w dużych wydajnych porcjach, a ostatnia 0x2 może to zrobić jako dwa pojedyncze bajty lub jako transfer 16-bitowy. Wyrównanie i cel wchodzą w grę, jeśli adresy źródłowy i docelowy są na tym samym wyrównaniu, powiedzmy 0x1003 i 0x2003, wtedy możesz zrobić jeden bajt, następnie 0x40 w dużych porcjach, a następnie 0x2, ale jeśli jeden to 0x1002, a drugi 0x1003, to dostaje naprawdę brzydkie i naprawdę wolne.
Przez większość czasu są to cykle autobusowe. Lub gorsza liczba przelewów. Weź procesor z 64-bitową magistralą danych, taką jak ARM, i wykonaj transfer czterech słów (odczyt lub zapis, LDM lub STM) pod adresem 0x1004, to jest adres wyrównany do słów i całkowicie legalny, ale jeśli magistrala ma 64 szerokość bitów jest prawdopodobne, że pojedyncza instrukcja zamieni się w trzy transfery w tym przypadku 32-bitowy przy 0x1004, 64-bitowy przy 0x1008 i 32-bitowy przy 0x100A. Ale gdybyś miał tę samą instrukcję, ale pod adresem 0x1008, mógłby wykonać pojedynczy transfer czterech słów pod adresem 0x1008. Każdy transfer ma przypisany czas konfiguracji. Tak więc różnica adresów od 0x1004 do 0x1008 może być kilka razy szybsza, nawet / esp podczas korzystania z pamięci podręcznej i wszystkie są trafieniami do pamięci podręcznej.
Mówiąc o tym, nawet jeśli wykonasz dwa słowa odczytane pod adresem 0x1000 vs 0x0FFC, 0x0FFC z brakami pamięci podręcznej spowoduje odczyt dwóch linii pamięci podręcznej, gdzie 0x1000 to jedna linia pamięci podręcznej, i tak zostaniesz ukarany za odczytywanie linii losowej losowo dostęp (odczyt większej ilości danych niż używanie), ale to podwaja się. Sposób, w jaki struktury są wyrównane lub dane w ogóle, a także częstotliwość uzyskiwania dostępu do tych danych itp., Mogą powodować przeładowanie pamięci podręcznej.
Możesz skończyć z rozbijaniem danych, tak że podczas przetwarzania danych możesz tworzyć eksmisje, możesz mieć naprawdę pecha i skończyć z wykorzystaniem tylko niewielkiej części pamięci podręcznej, a podczas przeskakiwania przez nią następna kropla danych zderza się z poprzednią kroplą . Przez zmieszanie danych lub ponowne uporządkowanie funkcji w kodzie źródłowym itp. Możesz tworzyć lub usuwać kolizje, ponieważ nie wszystkie pamięci podręczne są równe, kompilator nie pomoże ci tutaj. Nawet wykrywanie spadku wydajności lub poprawy zależy od Ciebie.
Wszystko, co dodaliśmy, aby poprawić wydajność, szersze magistrale danych, potoki, pamięci podręczne, przewidywanie rozgałęzień, wiele jednostek / ścieżek wykonawczych itp. Najczęściej pomogą, ale wszystkie mają słabe punkty, które można wykorzystać celowo lub przypadkowo. Kompilator lub biblioteki niewiele mogą na to poradzić, jeśli interesuje Cię wydajność, musisz dostroić, a jednym z największych czynników dostrajających jest wyrównanie kodu i danych, a nie tylko 32, 64, 128, 256 granice bitów, ale także tam, gdzie rzeczy są względem siebie nawzajem, mocno używane pętle lub ponownie wykorzystywane dane nie powinny lądować w ten sam sposób pamięci podręcznej, każda z nich chce mieć własną. Kompilatory mogą pomóc np. W zamawianiu instrukcji dla architektury super skalarnej, ponownym rozmieszczaniu instrukcji, które nie mają znaczenia,
Największym niedopatrzeniem jest założenie, że procesor jest wąskim gardłem. Nie było to prawdą przez dekadę lub dłużej, karmienie procesora jest problemem i tam właśnie pojawiają się problemy, takie jak uderzenia wydajności wyrównania, przerzucanie pamięci podręcznej itp. Przy odrobinie pracy, nawet na poziomie kodu źródłowego, ponowne uporządkowanie danych w strukturze, porządkowanie deklaracji zmiennych / struktur, porządkowanie funkcji w kodzie źródłowym i trochę dodatkowego kodu do wyrównywania danych, może kilkukrotnie poprawić wydajność więcej.