Czy jest możliwe napisanie kodu (lub kompletnego oprogramowania zamiast fragmentu kodu), który nie będzie działał poprawnie, jeśli zostanie uruchomiony na procesorze, który ma mniej niż N liczby rdzeni? Bez wyraźnego sprawdzenia i celowego niepowodzenia:
JEŻELI (noOfCores <4) NASTĘPNIE nie działają poprawnie celowo
Patrzę na minimalne wymagania systemowe gry ( Dragon Age: Inkwizycja ) i określa minimum czterordzeniowy procesor. Wielu graczy twierdzi, że NIE działa na dwurdzeniowych procesorach, a NAWET na Intel Core i3s z dwoma fizycznymi i dwoma logicznymi rdzeniami. I to NIE jest problem mocy obliczeniowej.
Z mojego zrozumienia, wątki są całkowicie odizolowane od procesora przez system operacyjny, ponieważ nie można tego zrobić.
Żeby to wyjaśnić:
Ja nie pytać „Czy mogę dowiedzieć się liczby rdzeni procesora z kodem, a nie celowo?” ... Taki kod byłby w złym zamiarze (zmusza cię do zakupu droższego procesora do uruchomienia programu - bez potrzeby korzystania z mocy obliczeniowej). Pytam, czy twój kod, powiedzmy, ma cztery wątki i kończy się niepowodzeniem, gdy dwa wątki są uruchamiane na tym samym rdzeniu fizycznym (bez jawnego sprawdzania informacji o systemie i celowego niepowodzenia) .
Krótko mówiąc, czy może istnieć oprogramowanie, które wymaga wielu rdzeni bez potrzeby dodatkowej mocy obliczeniowej pochodzącej z wielu rdzeni? Wymagałoby to tylko N osobnych rdzeni fizycznych.
źródło
Odpowiedzi:
Może być to możliwe „przypadkowo” z nieostrożnym wykorzystaniem powinowactwa rdzenia. Rozważ następujący pseudokod:
Jeśli uruchomisz cztery z nich na dwurdzeniowym procesorze, albo coś pójdzie nie tak z ustawieniem powinowactwa rdzenia, albo skończysz z dwoma wątkami blokującymi dostępne rdzenie i dwoma wątkami, które nigdy nie zostaną zaplanowane. W żadnym momencie nie zapytano wyraźnie, ile rdzeni jest w sumie.
(Jeśli masz długotrwałe wątki, ustawienie powinowactwa procesora ogólnie poprawia przepustowość)
Pomysł, że firmy produkujące gry „zmuszają” ludzi do kupowania droższego sprzętu bez uzasadnionego powodu, nie jest zbyt prawdopodobny. Może tylko stracić ich klientów.
Edycja: ten post ma teraz 33 pozytywne głosy, co jest dość spore, biorąc pod uwagę, że opiera się na wyuczonym zgadywaniu!
Wygląda na to, że ludzie mają DA: źle uruchomić na systemach dwurdzeniowych: http://www.dsogaming.com/pc-performance-analyses/dragon-age-inquisition-pc-performance-analysis/ Ta analiza wspomina, że sytuacja znacznie się poprawia, jeśli włączony jest hyperthreading. Biorąc pod uwagę, że HT nie dodaje żadnych jednostek wydających instrukcje ani pamięci podręcznej, po prostu pozwala na uruchomienie jednego wątku, podczas gdy inny jest zawieszony w pamięci podręcznej, co zdecydowanie sugeruje, że jest on powiązany wyłącznie z liczbą wątków.
Inny plakat twierdzi, że zmiana sterowników grafiki działa: http://answers.ea.com/t5/Dragon-Age-Inquisition/Working-solution-for-Intel-dual-core-CPUs/td-p/3994141 ; Biorąc pod uwagę, że sterowniki graficzne są zwykle nędznym ulem szumowiny i wioski, nie jest to zaskakujące. Jeden notoryczny zestaw sterowników miał tryb „poprawny i wolny” w porównaniu z „szybkim i niepoprawnym”, który został wybrany, jeśli został wywołany z QUAKE.EXE. Jest całkiem możliwe, że sterowniki zachowują się inaczej dla różnej liczby pozornych procesorów. Być może (powrót do spekulacji) zastosowano inny mechanizm synchronizacji. Niewłaściwe użycie blokad ?
„Niewłaściwe użycie operacji podstawowych blokowania i synchronizacji” jest bardzo, bardzo częstym źródłem błędów. (Błąd, na który powinienem patrzeć w pracy podczas pisania, to „awaria, jeśli zmieniam ustawienia drukarki w tym samym czasie, co kończy się zadanie drukowania”).
Edycja 2: komentarze wspominają, że OS próbuje uniknąć głodu wątków. Zauważ, że gra może mieć swój wewnętrzny quasi-harmonogram do przypisywania pracy do wątków, i będzie podobny mechanizm w samej karcie graficznej (który w rzeczywistości jest własnym systemem wielozadaniowym). Szanse na błąd w jednym z nich lub interakcja między nimi są dość wysokie.
www.ecsl.cs.sunysb.edu/tr/ashok.pdf (2008) to praca dyplomowa na temat lepszego planowania kart graficznych, w której wyraźnie wspomniano, że zwykle używają planowania według kolejności zgłoszeń, co jest łatwe do wdrożenia w systemy nieprewencyjne. Czy sytuacja się poprawiła? Prawdopodobnie nie.
źródło
Może być konieczne posiadanie 4 rdzeni, ponieważ aplikacja uruchamia cztery zadania w równoległych wątkach i oczekuje, że zakończą się prawie jednocześnie.
Gdy każdy wątek jest wykonywany przez osobny rdzeń, a wszystkie wątki mają dokładnie takie samo obciążenie obliczeniowe, istnieje duże prawdopodobieństwo (ale nie jest to gwarantowane), że zakończą się mniej więcej w tym samym czasie. Ale kiedy dwa wątki działają na jednym rdzeniu, taktowanie będzie znacznie mniej przewidywalne, ponieważ rdzeń będzie cały czas przełączał kontekst między dwoma wątkami.
Błędy występujące z powodu nieoczekiwanego czasu wątku są nazywane „ warunkami wyścigu ”.
W kontekście rozwoju gry jedną z możliwych architektur z tego rodzaju problemem może być ta, w której różne funkcje gry są symulowane w czasie rzeczywistym przez różne wątki procesora. Gdy każda funkcja działa na własnym rdzeniu, wszystkie są symulowane z mniej więcej taką samą prędkością. Ale gdy dwie funkcje działają na jednym rdzeniu, obie będą symulowane tylko o połowę szybciej niż reszta świata gry, co może powodować różnego rodzaju dziwne zachowania.
Zauważ, że architektura oprogramowania, która zależy od niezależnych wątków działających w określonych momentach czasowych, jest wyjątkowo delikatna i jest oznaką bardzo złego zrozumienia współbieżnego programowania. W praktycznie wszystkich wielowątkowych interfejsach API dostępne są funkcje umożliwiające jawną synchronizację wątków w celu zapobiegania tego rodzaju problemom.
źródło
Jest mało prawdopodobne, aby te „minimalne wymagania” reprezentowały coś, poniżej czego gra nie będzie działać. O wiele bardziej prawdopodobne jest to, że reprezentują coś, poniżej którego gra nie będzie działać z zadowalającą wydajnością. Żadna firma produkująca gry nie chce mieć do czynienia z wieloma klientami narzekającymi na kiepską wydajność, gdy działają na jednym rdzeniu 1 GHz, nawet jeśli oprogramowanie może technicznie działać. Prawdopodobnie więc celowo projektują tak, aby zawiodły mocno na urządzeniach z mniejszą liczbą rdzeni, niż dałoby to akceptowalną wydajność.
Jednym z ważnych wskaźników wydajności gry jest liczba klatek na sekundę. Zazwyczaj działają z prędkością 30 lub 60 klatek na sekundę. Oznacza to, że silnik gry musi renderować bieżący widok ze stanu gry w ustalonym czasie. Aby osiągnąć 60 fps, ma to nieco ponad 16 ms. Gry z wysokiej klasy grafiką są wyjątkowo związane z procesorem, więc istnieje ogromna korzyść między próbą podniesienia jakości (co zajmuje więcej czasu) a potrzebą pozostania w tym budżecie czasowym. W związku z tym budżet czasowy dla każdej klatki jest wyjątkowo napięty.
Ponieważ budżet czasu jest napięty, deweloper idealnie chce wyłącznego dostępu do jednego lub więcej rdzeni. Prawdopodobnie chcą też móc wykonywać swoje renderingi wyłącznie w rdzeniu, ponieważ to właśnie należy zrobić przy tym budżecie czasu, podczas gdy inne rzeczy, takie jak obliczanie stanu świata, odbywają się w osobnym procesie, w którym nie będzie to możliwe niepokoić.
Teoretycznie możesz wcisnąć to wszystko w jeden rdzeń, ale wtedy wszystko staje się znacznie trudniejsze. Nagle musisz upewnić się, że wszystkie rzeczy związane z grą zdarzają się wystarczająco szybko i pozwalają na renderowanie. Nie możesz po prostu zrobić z nich dwóch wątków oprogramowania, ponieważ nie ma sposobu, aby system operacyjny zrozumiał, że „wątek A musi wykonać X pracy w 16 ms, niezależnie od tego, co robi wątek B”.
Twórcy gier nie są zainteresowani kupowaniem nowego sprzętu. Powodem, dla którego mają wymagania systemowe, jest to, że koszt obsługi maszyn niższej klasy nie jest tego wart.
źródło
Trzy wątki w czasie rzeczywistym, które nigdy nie śpią i jeden inny wątek. Jeśli jest mniej niż cztery rdzenie, czwarty wątek nigdy się nie uruchamia. Jeśli czwarty wątek musi komunikować się z jednym z wątków w czasie rzeczywistym, aby zakończyć wątek w czasie rzeczywistym, kod nie zakończy się z mniej niż czterema rdzeniami.
Oczywiście, jeśli wątki w czasie rzeczywistym czekają na coś, co nie pozwala im spać (np. Spinlock), projektant programu spieprzył.
źródło
Przede wszystkim wątki programowe nie mają nic wspólnego z wątkami sprzętowymi i często są pomieszane. Wątki oprogramowania to fragmenty kodu, które można wysłać i uruchomić samodzielnie w kontekście procesu. Wątki sprzętowe są w większości zarządzane przez system operacyjny i są wysyłane do rdzenia procesora, gdy mówimy o zwykłych programach. Te wątki sprzętowe są wysyłane na podstawie obciążenia; sprzętowy dyspozytor wątków działa mniej więcej jak moduł równoważenia obciążenia.
Jednak jeśli chodzi o gry, szczególnie wysokiej klasy, czasami wątkami sprzętowymi zarządza sama gra lub gra instruuje dyspozytora wątków sprzętowych, co ma robić. Jest tak, ponieważ każde zadanie lub grupa zadań nie ma takiego samego priorytetu jak w normalnym programie. Ponieważ Dragon Age pochodzi z wysokiej klasy studia gier używającego wysokiej klasy silników do gier, mogę sobie wyobrazić, że używa on „ręcznej” wysyłki, a wtedy liczba rdzeni staje się minimalnym wymaganiem systemowym. Dowolny program ulegnie awarii, gdy wyślę kawałek kodu do 3. fizycznego rdzenia działającego na maszynie z tylko 1 lub 2 rdzeniami.
źródło
Ponieważ możliwe jest użycie wirtualizacji w celu uzyskania większej liczby rdzeni wirtualnych niż fizycznych, a oprogramowanie nie wiedziałoby, że działa na wirtualizacji i zamiast tego uważa, że ma tak wiele rdzeni fizycznych, powiedziałbym, że takie oprogramowanie nie jest możliwe.
Oznacza to, że nie można napisać oprogramowania, które zawsze zatrzyma się na rdzeniach mniejszych niż N.
Jak zauważyli inni, istnieją rozwiązania programowe, które mogą potencjalnie sprawdzić, szczególnie jeśli używany system operacyjny i kod mają niewielką ochronę przed warunkami wyścigu, gdy N procesów działa na <N procesorach. Prawdziwą sztuczką jest kod, który zawiedzie, gdy masz mniej niż N procesorów, ale nie zawiedzie, gdy masz N procesorów, ale system operacyjny, który może przypisać pracę mniej niż N procesorom.
źródło
Możliwe, że są trzy wątki, które coś robią (generują tła lub generują ruch NPC) i przekazują zdarzenia do czwartej, która ma agregować / filtrować zdarzenia i aktualizować model widoku. Jeśli czwarty wątek nie otrzyma wszystkich zdarzeń (ponieważ nie jest zaplanowany na rdzeniu), model widoku nie zostanie poprawnie zaktualizowany. Może się to zdarzać sporadycznie, ale rdzenie te muszą być dostępne w dowolnym momencie. To może wyjaśniać, dlaczego przez cały czas nie widzisz dużego obciążenia procesora, ale gra i tak nie działa poprawnie.
źródło
Myślę, że Joshua zmierza właściwą ścieżką, ale nie do końca.
Załóżmy, że masz architekturę, w której są napisane trzy wątki, które robią tyle, ile mogą - kiedy skończą to, co robią, robią to ponownie. Aby zwiększyć wydajność, wątki te nie zwalniają kontroli - nie chcą ryzykować opóźnień w harmonogramie zadań systemu Windows. Tak długo, jak są 4 lub więcej rdzeni, działa to dobrze, nie działa źle, jeśli nie ma.
Zasadniczo byłoby to złe programowanie, ale gry to inna sprawa - gdy masz do wyboru między projektem gorszym na całym sprzęcie lub projektem, który jest lepszy na wystarczająco dobrym sprzęcie lub awarią na gorszym sprzęcie twórcy gier zwykle wybierają wymagać sprzętu.
źródło
Is it possible to write code (or complete software, rather than a piece of code) that won't work properly when run on a CPU that has less than N number of cores?
Absolutnie. Wykorzystanie wątków w czasie rzeczywistym byłoby dobrym przykładem sytuacji, w której jest to nie tylko możliwe, ale pożądany (i często jedyny właściwy) sposób wykonania zadania. Jednak wątki w czasie rzeczywistym są zwykle ograniczone do jądra systemu operacyjnego, zwykle w przypadku sterowników, które muszą być w stanie zagwarantować, że jakieś zdarzenie sprzętowe zostanie obsłużone w określonym czasie. Nie powinieneś mieć wątków w czasie rzeczywistym w normalnych aplikacjach użytkownika i nie jestem pewien, czy można je nawet mieć w aplikacji w trybie użytkownika Windows. Zasadniczo systemy operacyjne celowo uniemożliwiają wykonanie tego z gruntów użytkowników właśnie dlatego, że pozwalają danej aplikacji przejąć kontrolę nad systemem.
W odniesieniu do aplikacji przeznaczonych dla użytkowników: Twoje założenie, że sprawdzanie określonej liczby wątków w celu uruchomienia jest z konieczności złośliwe, nie jest prawidłowe. Na przykład możesz mieć 2 długotrwałe zadania wymagające dużej wydajności, które wymagają dla siebie rdzenia. Bez względu na szybkość rdzenia procesora współdzielenie rdzenia z innymi wątkami może być poważnym i niedopuszczalnym spadkiem wydajności z powodu przeładowania pamięci podręcznej wraz z normalnymi karami nakładanymi za przełączanie wątków (które są dość znaczne). W takim przypadku byłoby to całkowicie uzasadnione, szczególnie w grze, aby ustawić każdy z tych wątków tak, aby miał powinowactwo tylko do jednego konkretnego rdzenia dla każdego z nich, a następnie ustawić wszystkie inne wątki, aby nie miały powinowactwa do tych 2 rdzeni. Aby to zrobić, musisz „
źródło
Każdy kod używający blokad z dowolną zauważalną ilością rywalizacji o blokadę będzie działał strasznie (do tego stopnia, że w przypadku aplikacji takich jak gra można powiedzieć „nie działa” ), jeśli liczba wątków przekroczy liczbę rdzeni.
Wyobraź sobie na przykład wątek producenta wysyłający zadania do kolejki obsługującej 4 wątki konsumenckie. Istnieją tylko dwa rdzenie:
Producent próbuje uzyskać blokadę, ale jest ona utrzymywana przez konsumenta działającego na drugim rdzeniu. Dwa rdzenie wykonują blokadę, gdy producent kręci się, czekając na zwolnienie blokady. To już jest złe, ale nie tak złe, jak się da.
Niestety, wątek konsumencki jest u kresu swojego czasu kwantowego, więc jest zapobiegany i planowany jest inny wątek konsumencki. Próbuje złapać zamek, ale oczywiście zamek jest zajęty, więc teraz dwa rdzenie wirują i czekają na coś, co nie może się zdarzyć.
Wątek producenta osiąga koniec przedziału czasowego i jest zapobiegany, budzi się inny konsument. Znowu dwóch konsumentów czeka na zwolnienie blokady, i to się nie stanie przed upływem dwóch kolejnych kwantów.
[...] Wreszcie konsument trzymający blokadę zwolnił blokadę. Jest natychmiast brany przez każdego, kto obraca się na drugim rdzeniu. Istnieje 75% szans (3 do 1), że to kolejny wątek konsumencki. Innymi słowy, jest 75% prawdopodobne, że producent nadal jest zawieszony. Oczywiście oznacza to, że konsumenci również zwlekają. Bez uciążliwych zadań producenta nie mają nic do roboty.
Zauważ, że działa to w zasadzie z każdym rodzajem blokady, nie tylko spinlockami - ale dewastujący efekt jest znacznie bardziej widoczny w przypadku spinlocków, ponieważ procesor utrzymuje cykle spalania, podczas gdy nic nie osiąga.
Teraz wyobraź sobie, że oprócz powyższego jakiś programista miał genialny pomysł, aby użyć dedykowanego wątku z powinowactwem ustawionym na pierwszy rdzeń, więc RDTSC da wiarygodne wyniki na wszystkich procesorach (i tak nie będzie, ale niektórzy tak myślą).
źródło
Jeśli rozumiem, o co pytasz, jest to możliwe, ale jest to bardzo, bardzo zła rzecz.
Kanonicznym przykładem tego, co opisujesz, byłoby utrzymanie licznika, który jest zwiększany przez wiele wątków. Nie wymaga to prawie nic w zakresie mocy obliczeniowej, ale wymaga starannej koordynacji między wątkami. Tak długo, jak narasta tylko jeden wątek naraz (w rzeczywistości jest to odczyt, po którym następuje dodanie, po którym następuje zapis), jego wartość zawsze będzie poprawna. Wynika to z faktu, że jeden wątek zawsze odczytuje poprawną wartość „poprzednia”, dodaje jeden i zapisuje prawidłową wartość „następną”. Weź dwa wątki do akcji jednocześnie i oba będą czytać tę samą „poprzednią” wartość, uzyskaj ten sam wynik z przyrostu i zapisz tę samą „następną” wartość. Licznik zostanie skutecznie zwiększony tylko raz, mimo że dwa wątki myślą, że każdy to zrobił.
Ta zależność między czasem a poprawnością jest tym, co informatyka nazywa wyścigiem .
Często eliminuje się warunki wyścigu, stosując mechanizmy synchronizacji, aby upewnić się, że wątki chcące operować na kawałku współdzielonych danych muszą się połączyć w celu uzyskania dostępu. Licznik opisany powyżej może w tym celu użyć blokady odczytu i zapisu .
Bez dostępu do wewnętrznego projektu Dragon Age: Inkwizycja wszystko, co można zrobić, to spekulować na temat tego, jak się zachowuje. Ale spróbuję na podstawie kilku rzeczy, które widziałem w moim własnym doświadczeniu:
Możliwe, że program jest oparty na czterech dostrojonych wątkach, więc wszystko działa, gdy wątki działają prawie nieprzerwanie na ich własnych rdzeniach fizycznych. „Dostrajanie” może polegać na przestawianiu kodu lub wstawianiu snu w strategiczne miejsca, aby złagodzić błędy spowodowane przez rasę, które pojawiły się podczas opracowywania. Ponownie, to wszystko przypuszczenie, ale widziałem, że warunki wyścigowe „rozwiązały się” w ten sposób więcej razy, niż chciałbym policzyć.
Uruchomienie takiego programu na czymkolwiek mniej zdolnym niż środowisko, dla którego został dostrojony, wprowadza zmiany czasowe, które są wynikiem tego, że kod nie działa tak szybko lub, co bardziej prawdopodobne, przełącza kontekst. Przełączanie kontekstu zachodzi w sposób fizyczny (tj. Fizyczne rdzenie procesora przełączają się między pracą, którą wstrzymują jego rdzenie logiczne) i logiczną (tj. System operacyjny w CPU przypisuje pracę do rdzeni) na różne sposoby, ale jest to znacząca rozbieżność od będzie „oczekiwanym” czasem wykonania. To może wywołać złe zachowanie.
Jeśli Dragon Age: Inkwizycja nie wykona prostego kroku, aby upewnić się, że przed kontynuowaniem jest wystarczająca liczba rdzeni fizycznych, to wina EA. Prawdopodobnie spędzają małą fortunę na polowaniu na połączenia i wiadomości e-mail od osób, które próbowały uruchomić grę na zbyt małym sprzęcie.
źródło
System Windows ma wbudowaną funkcjonalność: funkcja GetLogicalProcessorInformation znajduje się w interfejsie API systemu Windows . Możesz wywołać go ze swojego programu, aby uzyskać informacje na temat rdzeni, rdzeni wirtualnych i hiperwątkowości.
Tak więc odpowiedź na twoje pytanie brzmi: tak.
źródło
/proc/cpuinfo
isysconf(_SC_NPROCESSORS_ONLN)
(ten ostatni jest wymieniony w POSIX). Korzystanie z informacji w celu wymuszenia minimalnego progu wydajności jest jednak dość złą formą.