ogólnej sytuacjiJak skwapliwie zatwierdzić przydzieloną pamięć w C++?
Aplikacja jest niezwykle intensywny zarówno przepustowość, wykorzystanie procesora oraz wykorzystanie GPU musi przenieść około 10-15GB na sekundę z jednego do drugiego GPU. Używa API DX11 do uzyskania dostępu do GPU, więc przesyłanie do GPU może się odbywać tylko w przypadku buforów, które wymagają mapowania dla każdego pojedynczego przesłania. Przesyłanie odbywa się w porcjach po 25 MB na raz, a 16 wątków zapisuje bufory jednocześnie do odwzorowanych buforów. Niewiele można z tym zrobić. Rzeczywisty poziom współbieżności zapisów powinien być mniejszy, gdyby nie następujący błąd.
To zgrabna stacja robocza z 3 procesorami graficznymi Pascal, wysokiej klasy procesorem Haswell i czterokanałową pamięcią RAM. Niewiele można poprawić na sprzęcie. To prowadzenie edycja pulpitu Windows 10.
rzeczywisty problem
Gdy mijam ~ 50% obciążenia procesora, coś w MmPageFault()
(wewnątrz jądra systemu Windows, o nazwie przy dostępie do pamięci, które zostały przypisane do Twojego przestrzeń adresowa, ale jeszcze nie została zatwierdzona przez system operacyjny), strasznie się psuje, a pozostałe 50% obciążenia procesora marnuje się na spin-lock wewnątrz MmPageFault()
. Procesor zostaje w 100% wykorzystany, a wydajność aplikacji całkowicie spada.
Muszę założyć, że wynika to z ogromnej ilości pamięci, która musi zostać przydzielona do procesu w każdej sekundzie, a która jest całkowicie niezmapowana z procesu za każdym razem, gdy bufor DX11 nie jest mapowany. Odpowiednio, to jest rzeczywiście tysiące połączeń do MmPageFault()
na sekundę, dzieje się sekwencyjnie jako memcpy()
jest zapisywanie sekwencyjnie do bufora. Dla każdej napotkanej niezaakceptowanej strony.
Jedno obciążenie procesora przekracza 50%, optymistyczny spin-lock w jądrze systemu Windows chroni zarządzanie stroną całkowicie pogarsza wydajność.
Rozważania
Bufor jest alokowany przez kierowcę DX11. Nic nie może zostać poprawione o strategii alokacji. Używanie innego API pamięci, a zwłaszcza ponowne użycie, nie jest możliwe.
Połączenia z interfejsem API DX11 (mapowanie/usuwanie map buforów) wszystko dzieje się z jednego wątku. Faktyczne operacje kopiowania mogą potencjalnie przebiegać wielowątkowo w większej liczbie wątków niż w systemie są procesory wirtualne.
Zmniejszenie wymagań dotyczących przepustowości pamięci nie jest możliwe. Jest to aplikacja działająca w czasie rzeczywistym. W rzeczywistości twardym limitem jest obecnie przepustowość PCIe 3.0 16x podstawowego procesora graficznego. Gdybym mógł, już musiałbym naciskać dalej.
Unikanie kopii wielowątkowych nie jest możliwe, ponieważ istnieją niezależne kolejki producent-konsument, które nie mogą być łączone w sposób trywialny.
Spadek wydajności spin-lock wydaje się tak rzadki (ponieważ przypadek użycia przesuwa go tak daleko), że w Google nie można znaleźć pojedynczego wyniku dla nazwy funkcji spin-lock.
Trwa aktualizowanie do interfejsu API, który zapewnia większą kontrolę nad odwzorowaniami (Vulkan), ale nie jest to rozwiązanie krótkoterminowe. Przejście na lepsze jądro systemu operacyjnego nie jest obecnie możliwe z tego samego powodu.
Zmniejszenie obciążenia procesora również nie działa; jest zbyt wiele pracy, którą należy wykonać poza (zwykle banalną i niedrogą) kopią bufora.
Pytanie
Co można zrobić?
Potrzebuję znacznie zmniejszyć liczbę pojedynczych błędów stron. Znam adres i rozmiar bufora, który został zmapowany do mojego procesu, a także wiem, że pamięć nie została jeszcze zatwierdzona.
Jak mogę zagwarantować, że pamięć zostanie zatwierdzona przy jak najmniejszej ilości transakcji?
Egzotyczne flagi dla DX11, które zapobiegają de-alokacji buforów po ich usunięciu, interfejsy API systemu Windows wymuszają zatwierdzanie w pojedynczej transakcji, prawie wszystko jest mile widziane.
Obecny stan
// In the processing threads
{
DX11DeferredContext->Map(..., &buffer)
std::memcpy(buffer, source, size);
DX11DeferredContext->Unmap(...);
}
To brzmi jak masz około 400 M dla wszystkich 16 wątków razem. Całkiem nisko. Czy możesz zweryfikować, że nie przekroczyłeś tego w swojej aplikacji? Jakie jest tam zużycie pamięci wybierania? Zastanawiam się, czy masz wyciek pamięci. – Serge
Maksymalne zużycie wynosi około 7-8 GB, ale to normalne, biorąc pod uwagę, że w sumie cały proces przetwarzania potrzebuje> 1 s buforowania, aby zrekompensować wszystkie wąskie gardła. Tak, to "tylko" 400 MB, 25 razy na sekundę. I działa dobrze, dopóki podstawowe obciążenie procesora nie przekroczy 50%, a wydajność blokady spinu nagle wzrasta z praktycznie 0 do 40-50% całkowitego wykorzystania procesora. Wpływa również na inne procesy w systemie w tym samym czasie. – Ext3h
1. Jaka jest twoja pamięć fizyczna? czy możesz zabić wszystkie inne aktywne procesy? 2. zgadnij # 2, ponieważ widzisz próg 50%, możesz dostać się do pewnych problemów z hyperthreading. Ile masz fizycznych rdzeni? 8? Czy możesz wyłączyć hyperthreading? Spróbuj uruchomić tyle wątków, ile fizycznych cpusów znajduje się w twoim przypadku na czystej maszynie. – Serge