Rozważ następujący bardzo prosty program komputerowy:
for i = 1 to n:
y[i] = x[p[i]]
Tutaj i y są n -elementowe tablice bajtów, a P jest N -elementowe szereg słów. Tutaj n jest duże, np. N = 2 31 (tak, że tylko niewielka część danych mieści się w jakiejkolwiek pamięci podręcznej).
Załóżmy, że składa się z liczb losowych , równomiernie rozmieszczonych między 1 i n .
Z punktu widzenia nowoczesnego sprzętu powinno to oznaczać:
- odczyt jest tani (odczyt sekwencyjny)
- odczyt jest bardzo kosztowny (losowe odczyty; prawie wszystkie odczyty są błędami pamięci podręcznej; będziemy musieli pobrać każdy pojedynczy bajt z pamięci głównej)
- pisanie jest tanie (zapis sekwencyjny).
I rzeczywiście to obserwuję. Program działa bardzo wolno w porównaniu z programem, który wykonuje tylko sekwencyjne operacje odczytu i zapisu. Świetny.
Teraz pojawia się pytanie: jak dobrze ten program działa równolegle na nowoczesnych platformach wielordzeniowych?
Moja hipoteza była taka, że ten program nie działa dobrze równolegle. W końcu wąskim gardłem jest pamięć główna. Jeden rdzeń już marnuje większość czasu, czekając tylko na dane z pamięci głównej.
Nie tego jednak zaobserwowałem, gdy zacząłem eksperymentować z niektórymi algorytmami, w których wąskim gardłem była tego rodzaju operacja!
Po prostu zamieniłem naiwną pętlę for na równoległą pętlę OpenMP (w zasadzie podzieli on zakres na mniejsze części i równolegle uruchomię te części na różnych rdzeniach procesora).
Na niskich komputerach przyspieszenia były rzeczywiście niewielkie. Ale na platformach wyższej klasy byłem zaskoczony, że otrzymałem doskonałe przyspieszenia prawie liniowe. Kilka konkretnych przykładów (dokładne czasy mogą być nieco opóźnione, istnieje wiele losowych odmian; były to tylko szybkie eksperymenty):
2 x 4-rdzeniowy Xeon (w sumie 8 rdzeni): współczynnik 5-8 przyspieszeń w porównaniu z wersją jednowątkową.
2 x 6-rdzeniowy Xeon (łącznie 12 rdzeni): współczynnik 8-14 przyspieszeń w porównaniu z wersją jednowątkową.
To było zupełnie nieoczekiwane. Pytania:
Właśnie dlaczego taki program jest tak równoległy ? Co dzieje się w sprzęcie? (Moje obecne przypuszczenie jest coś w tym rodzaju: losowe odczyty z innego wątku są „potokowe”, a średni wskaźnik uzyskiwania odpowiedzi na te pytania jest znacznie wyższy niż w przypadku pojedynczego wątku.)
Czy konieczne jest użycie wielu wątków i wielu rdzeni, aby uzyskać jakieś przyspieszenia? Jeśli w interfejsie między pamięcią główną a procesorem rzeczywiście zachodzi jakiś potok, czy aplikacja jednowątkowa nie może poinformować pamięci głównej, że wkrótce będzie potrzebować , x [ p [ i + 1 ] ] , ... a komputer może rozpocząć pobieranie odpowiednich linii pamięci podręcznej z pamięci głównej? Jeśli jest to w zasadzie możliwe, jak mogę to osiągnąć w praktyce?
Jaki jest właściwy model teoretyczny , którego moglibyśmy użyć do analizy tego rodzaju programów (i do prawidłowego przewidywania wydajności)?
Edycja: Teraz jest dostępny kod źródłowy i wyniki testów porównawczych tutaj: https://github.com/suomela/parallel-random-read
Niektóre przykłady figurek z boiska ( ):
- około. 42 ns na iterację (losowy odczyt) z jednym wątkiem
- około. 5 ns na iterację (losowy odczyt) z 12 rdzeniami.