Programy, które twierdzą, że nie są przyjazne dla wielu rdzeni

17

Od czasu do czasu widzisz to zdanie lub podobne, co ogólnie odnosi się do programu, który twierdzi, że nie zostały zaprojektowane w celu pełnego wykorzystania możliwości procesorów wielordzeniowych. Jest to powszechne zwłaszcza w przypadku programowania gier wideo. (oczywiście wiele programów nie ma współbieżności i nie potrzebuje jej, takich jak podstawowe skrypty itp.).

Jak to może być? Wiele programów (zwłaszcza gier) z natury korzysta z współbieżności, a ponieważ system operacyjny jest odpowiedzialny za planowanie zadań na CPU, to czy te programy z natury nie korzystają z wielu dostępnych rdzeni? Co w tym kontekście oznaczałoby „wykorzystanie wielu rdzeni”? Czy ci programiści faktycznie zabraniają planowania zadań systemu operacyjnego i wymuszają powinowactwo lub własne planowanie? (Brzmi jak poważny problem ze stabilnością).

Jestem programistą Java, więc może nie musiałem sobie z tym poradzić z powodu abstrakcji lub czegokolwiek innego.

SnakeDoc
źródło
11
Dużą możliwością jest to, że w synchronizacji wykorzystano skróty, które działają w systemie z jednym procesorem / rdzeniem, ale zrywają z prawdziwą współbieżnością wielu procesorów / rdzeni.
Bart van Ingen Schenau
@BartvanIngenSchenau: To prawda. Powinieneś to rozwinąć i opublikować jako odpowiedź. Myślę, że wszyscy inni pominęli ten punkt.
kevin cline
1
Myślę, że @Bart jest naprawdę blisko. Jednak s / work / wydaje się działać / i będzie bliżej znaku.
Ben Voigt
na marginesie - miałem to doświadczenie jako użytkownik, a nie programista - Ground Control 2 na Windows XP. Musiałem ustawić powinowactwo rdzenia tylko do jednego rdzenia w systemie wielordzeniowym, aby działało ono poprawnie, w przeciwnym razie wszystkie animacje (w rzeczywistości całej gry) działałyby z prędkością 10x, co, będąc większym wyzwaniem, po pewnym czasie stało się nieco denerwujące . Nie wykonałem żadnej pracy nad grami, ale moim zdaniem część gry polegała na tym, że procesor wykonywał tylko pewną ilość pracy w tym samym czasie.
jammypeach

Odpowiedzi:

28

Dobra współbieżność wymaga znacznie więcej niż rzucania kilku wątków w aplikację i liczenia na najlepsze. Istnieje pewien zakres, w jakim program może iść od żenująco równoległego do czysto sekwencyjnego. Każdy program może korzystać z prawa Amdahla do wyrażenia skalowalności problemu lub algorytmu. Kilka kwalifikacji do żenująco równoległego wniosku to:

  • Brak stanu wspólnego, każda funkcja zależy tylko od przekazanych parametrów
  • Brak dostępu do urządzeń fizycznych (kart graficznych, dysków twardych itp.)

Istnieją inne kwalifikacje, ale dzięki tym dwóm możemy zrozumieć, dlaczego gry nie są tak łatwe, jak mogłoby się wydawać, aby skorzystać z wielu rdzeni. Po pierwsze, model świata, który będzie renderowany, musi być współdzielony, ponieważ różne funkcje obliczają fizykę, ruch, stosują sztuczną inteligencję itp. Po drugie, każda klatka tego modelu gry musi być renderowana na ekranie za pomocą karty graficznej.

Szczerze mówiąc, wielu twórców gier korzysta z silników gier produkowanych przez strony trzecie. Minęło trochę czasu, ale te silniki gier innych firm są teraz znacznie bardziej równoległe niż kiedyś.

W przypadku efektywnej współbieżności istnieją większe wyzwania architektoniczne

Współbieżność może przybierać różne formy, od uruchamiania zadań w tle do pełnego wsparcia architektury dla współbieżności. Niektóre języki zapewniają bardzo zaawansowane funkcje współbieżności, takie jak ERLANG , ale wymaga to odmiennego myślenia o sposobie tworzenia aplikacji.

Nie każdy program naprawdę potrzebuje złożoności pełnego wsparcia dla wielordzeniowych procesorów. Jednym z takich przykładów jest oprogramowanie podatkowe lub dowolna aplikacja sterowana formularzem. Gdy większość czasu spędzasz na oczekiwaniu na coś od użytkownika, złożoność aplikacji wielowątkowych nie jest tak przydatna.

Niektóre aplikacje są bardziej krępująco równoległe, na przykład aplikacje internetowe. W tym przypadku platforma zaczyna się żenująco równolegle i to od Ciebie nie musisz narzucać rywalizacji.

Dolna linia:

Nie wszystkie aplikacje są naprawdę uszkodzone, ponieważ nie korzystają z wielu wątków (a tym samym rdzeni). W przypadku tych, których to boli, czasami obliczenia nie są przyjazne dla równoległego przetwarzania lub narzutu w celu jego koordynacji sprawiłby, że aplikacja byłaby bardziej delikatna. Niestety równoległe przetwarzanie nie jest jeszcze tak łatwe, jak powinno być, aby zrobić dobrze.

Berin Loritsch
źródło
To świetna analiza. Jedną z rzeczy, które mnie denerwują, jest to, że programy w świecie rzeczywistym często nie są zawstydzająco równoległe, a tym samym trudne do zrównoleglenia: Chociaż równoległe wykonywanie tych samych czynności może być niemożliwe , równoległe wykonywanie różnych czynności może być bardzo łatwe ( np. w architekturze potokowej lub z oddzielnym wątkiem interfejsu użytkownika).
amon
8
Rzeczywistą rzeczą jest to, że musisz projektować do wykonania równoległego, a jeśli nie, jesteś ograniczony przez brak projektu. Zgadzam się, że równoległe wykonywanie różnych czynności może być bardzo łatwe, ale nie w przypadku istniejącej aplikacji o wysokich oczekiwaniach użytkowników. W takim przypadku bardzo dobrze może wymagać przepisania, aby było to możliwe. Ponowne zapisywanie jest z natury ryzykowne, ale czasami możesz zrobić dla nich dobry argument. Zrobiłem kilka takich przeróbek, które zmaksymalizowały równoległe przetwarzanie, jednocześnie zachowując jak najwięcej kodu. Istnieje wiele ukrytych czynników.
Berin Loritsch
Świetna odpowiedź. Warto podkreślić, że nie tylko mogą występować malejące zwroty w równoległości niektórych systemów, ale niektóre mogą w rzeczywistości stać się wolniejsze z powodu narzutu niezbędnego do ich równoległości. W szczególności wiele semaforów / blokad i przełączanie kontekstu może mieć niekorzystny wpływ na środowisko wykonawcze. Przełączanie kontekstu może w szczególności zmniejszyć efektywność pamięci podręcznej, co nie jest trywialne, jeśli jesteś w punkcie optymalizacji systemu. W szczególności przykład silników gier firmy OP przypomina mi, że słyszę o wiele więcej na temat optymalizacji buforowania niż dostępu równoległego.
Gankro
35

Wiele programów (zwłaszcza gier) z natury korzysta z współbieżności,

Nie, w rzeczywistości jest na odwrót. Większość aplikacji jest napisana w jednym wątku, a programiści nigdy nie wprowadzili niezbędnych zmian w celu obsługi współbieżności.

W językach C, C ++ i C # musisz wyraźnie powiedzieć aplikacji, aby uruchomiła nowe wątki i / lub procesy.

Myślę, że zbytnio koncentrujesz się na planowaniu wątków, a zbyt mało na przetwarzaniu danych w potencjalnych wątkach. Udostępnianie danych między wątkami i / lub procesami wymaga pewnej formy synchronizacji. Jeśli zmienisz aplikację tak, aby korzystała z wielu wątków, ale synchronizacja się nie powiedzie, prawdopodobnie będziesz mieć trudności z wyśledzeniem błędów w kodzie.

W przypadku wielowątkowych aplikacji, nad którymi pracowałem, generalnie nigdy nie martwiłem się o wysyłkę i tylko o synchronizację danych. Jedyną sytuacją, w której musiałem się martwić o wysyłkę, było ściganie warunków wyścigu z powodu nieprawidłowej synchronizacji danych.

Zasadniczo, gdy aplikacja mówi, że nie może używać wielu rdzeni, oznacza to, że nie ma synchronizacji w celu ochrony manipulacji danymi.


źródło
Dotyczy to nawet nowych nowoczesnych programów od dużych programistów / wydawców? Kiedy siadam i piszę program, jedną z pierwszych rzeczy na etapie projektowania, o której myślę, jest to, czy potrzebuję współbieżności? Ponieważ może to skutkować drastycznie inną konstrukcją. W szczególności gry muszą mieć pewien poziom współbieżności, w przeciwnym razie gra zawiesiłaby się, gdy jeden z tysięcy modeli ekranowych próbował coś zrobić ...?
SnakeDoc
5
@ SnakeDoc - Myślę, że mylisz tam swoje domeny. Firmy Big Game z pewnością piszą z myślą o współbieżności, ale jeszcze nie widziałem, aby gra z Big Game nie obsługiwała współbieżności. Aplikacje i gry, które widziałem i które nie mogą obsługiwać współbieżności, pochodzą zazwyczaj z mniejszych sklepów / indywidualnych deweloperów, w których nie zaczęliby z takim nastawieniem. W pewnym momencie ewolucji aplikacji po tym fakcie niemożliwe staje się połączenie. Niektóre aplikacje nigdy nie miały na celu wystarczającego usprawiedliwienia współbieżności.
A także niektóre gry rozwijają się dzięki nowej zawartości (grafika i rozgrywka), bez konieczności aktualizacji silnika gry (implementacja kodu). Dlatego silnik gry może być opóźniony o wiele lat w technologii.
rwong
6
@ SnakeDoc: Nie potrzebujesz współbieżności, aby radzić sobie z tysiącami modeli wyświetlanych na ekranie. To nie jest tak, że każdy obiekt w grze potrzebuje własnego wątku, aby go zasymulować; jeden wątek może obsłużyć aktualizacje wszystkiego na ekranie za każdym razem.
user2357112 obsługuje Monikę
13

Nie chodzi tu o wiele rdzeni, ale o wiele wątków. System operacyjny może zaplanować uruchamianie wątku na dowolnym rdzeniu, który mu się podoba, a harmonogram ten jest przejrzysty dla planowanego programu. Jednak wiele programów nie jest pisanych przy użyciu wielu wątków, więc mogą działać tylko na jednym rdzeniu jednocześnie.

Dlaczego miałbym pisać program jednowątkowy? Są łatwiejsze do napisania i łatwiejsze do debugowania: jedna rzecz dzieje się po drugiej (zamiast dzieje się wiele rzeczy naraz i możliwe wzajemne oddziaływanie). Lub twój program może nie być ukierunkowany na komputery wielordzeniowe (tak jak w przypadku starych gier). W niektórych przypadkach program wielowątkowy może nawet działać wolniej niż wersja jednowątkowa, jeśli narzut związany z przełączaniem kontekstu i komunikacja między wątkami przewyższa prędkość uzyskaną przez równoległe wykonywanie (niektóre części programu mogą nie być równoległe).

amon
źródło
8

To nie jest pełna odpowiedź. To jest przestroga.

Pewnego dnia pomyślałem, że pokażę studentom na moim równoległym kursie programowania równoległą szybką sesję. Myślałem, że Quicksort powinien się dobrze zrównoleglać. Użyłem dwóch wątków. Uruchomiłem go na moim komputerze z jednym rdzeniem. Wyniki były następujące:

  • 14 sekund dla wersji jednowątkowej.
  • 15 sekund dla wersji 2-wątkowej.

Chodziło o to, czego się spodziewałem.

Potem wypróbowałem to na nowszej, dwurdzeniowej maszynie.

  • 11 sekund dla wersji jednowątkowej.
  • 20 sekund dla wersji 2-wątkowej.

Dwa wątki dzieliły kolejkę pozostałych zadań. Wygląda na to, że pola obiektu kolejki były tasowane tam iz powrotem między pamięcią podręczną jednego rdzenia a drugim.

Theodore Norvell
źródło
2
Z iloma elementami tablicy testowałeś? Być może scalesort byłby bardziej odpowiedni, ponieważ programowanie wielordzeniowe wymagałoby kopiowania danych, aby uniknąć konfliktów linii cache?
rwong
2
@rwong Było 10 000 000 elementów tablicy. Z pewnością połączenie byłoby dobrze zrównoleglone. Gdybym użył sortowania scalającego, prawdopodobnie nie nauczyłbym się przydatnej lekcji.
Theodore Norvell
1
@ArlaudPierre Rozważę równoległość dowolnego algorytmu. Quicksort jest interesujący, ponieważ możesz do tego zastosować podejście work-of-task. Ponieważ zadania są niezależne, moja intuicja była taka, że ​​powinien to być przykład zawstydzającego paralelizmu. Powinienem wspomnieć, że po drobnym tuningu rzeczywiście przyspieszył prawie o 2
Theodore Norvell
1
@Jules Odpowiedzią jest równoważenie obciążenia. Chciałem też napisać to w taki sposób, aby liczba wątków była łatwa do zmiany. Twoje podejście ładnie uogólnia na potęgi 2, ale nie tak dobrze na inne liczby wątków.
Theodore Norvell
2
@MaciejPiechotka Moralność to właściwie wszystko, co sugerujesz. Ale wracając do OP, myślę, że najistotniejszym morałem jest to, że programy wielowątkowe mogą faktycznie działać (znacznie) wolniej na architekturze wielordzeniowej niż na procesorze jednordzeniowym, chyba że dołożono wszelkich starań, aby zapewnić inaczej.
Theodore Norvell