Co jest ważne przy optymalizacji pamięci podręcznej procesora (w C)?

13

Po przeczytaniu tych dwóch pytań widzę, że zrozumienie zachowania pamięci podręcznej procesora może być ważne, gdy mamy do czynienia z dużą ilością danych w pamięci. Chciałbym zrozumieć sposób działania buforowania, aby dodać kolejne narzędzie do mojego zestawu narzędzi optymalizacji.

Jakie są podstawowe punkty dotyczące sposobu działania pamięci podręcznej procesora, aby móc rozsądnie pisać kod, który z niego korzysta? Czy istnieje sposób na profilowanie kodu, aby sprawdzić, czy słabe użycie pamięci podręcznej spowalnia działanie?

Timothy Jones
źródło
Skrytki nie są wszędzie takie same; oczywiście różnią się wielkością. Nie oczekuj, że poznasz głębokie sekrety, tylko dobre praktyki (takie jak rada Michaela Borgwardta).
David Thornley,

Odpowiedzi:

17
  • Jeśli to możliwe, przechowuj małe dane
  • Zachowaj w pamięci elementy, które będą dostępne razem (lub bezpośrednio po drugim)
  • Dowiedz się o parametrach optymalizacyjnych kompilatora
  • Przeczytaj Co każdy programista powinien wiedzieć o pamięci, aby uzyskać więcej szczegółów, niż mógłbyś chcieć
Michael Borgwardt
źródło
+1 za „Zachowaj rzeczy, które będą dostępne razem obok siebie”; tego łatwo zapomnieć.
Donal Fellows,
I powiedz kompilatorowi, aby zoptymalizował.
prawej strony
@WTP: prawo - dodano.
Michael Borgwardt,
Również trzymaj muteksy dobrze oddzielone. Zmiana muteksu (powinna) opróżnić wszystkie linie pamięci podręcznej, w których się znajduje, na wszystkich procesorach. Może to być duży hit wydajności, jeśli udało się uzyskać 2-3 muteksy w jednej linii pamięci podręcznej.
Vatine
12

Skomplikowanie tej kwestii jest obecnie poza ludzkim zrozumieniem. (Tak było od ostatnich 5 lat.) Połącz to z równoległością krótkich wektorów (SIMD) i masz poczucie beznadziejności, że ręczna optymalizacja kodu nie jest już ekonomicznie wykonalna - nie, że nie jest to możliwe, ale byłoby nie są już opłacalne.

Obecne podejście polega na uczeniu komputerów, jak optymalizować - poprzez wprowadzanie odmian kodu, które obliczają te same odpowiedzi w różnych strukturach (pętle, struktura danych, algorytmy) i automatyczną ocenę wydajności. Reguły transformacji kodu są określone za pomocą bardzo rygorystycznego modelu matematycznego, dzięki czemu jest to coś, co obaj informatycy mogą zrozumieć, a komputery mogą wykonać.

Poniżej znajduje się link opublikowany przez Larry'ego OBriena w jednej z jego odpowiedzi .

http://onward-conference.org/2011/images/Pueschel_2011_AutomaticPerformanceProgramming_Onward11.pdf

rwong
źródło
2
najszybsza implementacja BLAS (GotoBLAS) wykorzystuje ręcznie zoptymalizowany kod, aby zapewnić maksymalne wykorzystanie pamięci podręcznej do mnożenia macierzy
quant_dev 14.01.12
2

Całkiem możliwe jest zrozumienie i optymalizacja pamięci podręcznych. Zaczyna się od zrozumienia sprzętu i kontynuuje kontrolę nad systemem. Im mniej masz kontroli nad systemem, tym mniejsze prawdopodobieństwo, że odniesiesz sukces. Linux lub Windows z uruchomionymi pakietami aplikacji / wątków, które nie pracują na biegu jałowym.

Większość pamięci podręcznych jest nieco podobnych pod względem właściwości. Użyj części pola adresu do wyszukiwania trafień, mają głębokość (sposoby) i szerokość (linia pamięci podręcznej). Niektóre mają bufory zapisu, niektóre można skonfigurować tak, aby zapisywały lub omijały pamięć podręczną zapisów itp.

Musisz być bardzo świadomy wszystkich transakcji pamięciowych, które dochodzą do tej pamięci podręcznej (niektóre systemy mają niezależne pamięci podręczne instrukcji i danych, co ułatwia zadanie).

Możesz łatwo uczynić pamięć podręczną bezużyteczną, nie starannie zarządzając pamięcią. Na przykład, jeśli masz wiele bloków danych, które przetwarzasz, mając nadzieję na zachowanie ich w pamięci podręcznej, ale są one w pamięci pod adresami, które są nawet wielokrotnościami względem sprawdzania trafień / braków w pamięci podręcznej, powiedzmy 0x10000 0x20000 0x30000, a masz więcej te niż sposoby w pamięci podręcznej mogą bardzo szybko spowodować, że coś będzie działało dość wolno przy włączonej pamięci podręcznej, wolniej niż przy wyłączonej pamięci podręcznej. Ale zmień to na być może 0x10000, 0x21000, 0x32000 i to może wystarczyć, aby w pełni wykorzystać pamięć podręczną, zmniejszając eksmisje.

Podsumowując, kluczem do optymalizacji pod kątem pamięci podręcznej (cóż, oprócz znajomości systemu całkiem dobrze) jest utrzymanie wszystkich rzeczy, dla których potrzebujesz wydajności w pamięci podręcznej w tym samym czasie, uporządkowanie tych danych w taki sposób, aby możliwe było posiadanie wszystko w pamięci podręcznej naraz. I zapobieganie eksmisji znacznych części tych danych, których używasz, takich jak wykonywanie kodu, przerwania i inne regularne lub losowe zdarzenia.

To samo dotyczy kodu. Jest to jednak trochę trudniejsze, ponieważ musisz kontrolować lokalizacje, w których żyje kod, aby uniknąć kolizji z innym kodem, który chcesz przechowywać w pamięci podręcznej. Podczas testowania / profilowania dowolnego kodu, który przechodzi przez pamięć podręczną, dodając tu i tam jeden wiersz kodu lub nawet pojedynczy nop, wszystko, co przesuwa lub zmienia adresy, w których kod znajduje się z jednej kompilacji na drugą dla tego samego kodu, zmienia się, gdy wiersze pamięci podręcznej mieszczą się w tym kodzie i zmieniają to, co zostanie eksmitowane, a co nie w przypadku sekcji krytycznych.

old_timer
źródło
1

Odpowiedzi zarówno Nwonga, jak i Michaela Borgwardta dają dobrą radę.

Najpierw zaufaj optymalizacjom kompilatora dotyczącym tych problemów.

Jeśli używasz najnowszego kompilatora GCC, możesz użyć (z parsimony) jego __builtin_prefetchfunkcji. Zobacz tę odpowiedź na stackoverflow.

Basile Starynkevitch
źródło