Wann wird eine Parallel-Tasks-Engine zu einer guten Lösung?

8

Ich bin oft versucht, das Spiel zu brechen, an dem ich arbeite, um eine auf parallelen Aufgaben basierende Architektur auszuprobieren, aber das scheint keine große Voraussetzung für mein Projekt zu sein, deshalb vermeide ich dies im Moment. Ich habe vor, es zuerst in einem Spielzeugprojekt zu versuchen, um "mit dem Konzept zu spielen".

Was ich jetzt frage, ist, dass viele (Nicht-AAA-) Spiele keine wirklich sehr hohe Leistung erfordern. Es scheint, dass es sich nicht lohnt, eine aufgabenbasierte Engine zu verwenden, bis ... in welchen Fällen?

Im Moment vermute ich nur, dass es notwendig ist, wenn Sie wirklich die maximale Leistung der (Multi-Core-) Hardware nutzen müssen. Gibt es andere Fälle, in denen es eine gute Idee ist, eine aufgabenbasierte Engine zu verwenden? Oder ist es für neue Projekte immer gut, mit einer aufgabenbasierten Engine zu beginnen?

Klaim
quelle

Antworten:

7

Das Einfädeln wird in zwei Situationen verwendet:

  1. Wenn Sie viel zu tun haben, aber die Reaktionsfähigkeit des Benutzers beibehalten müssen.
  2. Wenn Sie die Leistung von mehr als einem Prozessorkern auf Ihrer bevorzugten Plattform / Mindestanforderung benötigen.

Wenn Sie vernünftigerweise erwarten können, dass eines oder beide dieser Dinge notwendig sind, dann entscheiden Sie sich dafür. Sonst nicht. Natürlich wird Sie niemand daran hindern, zum Spaß zu fädeln, weil Sie es können, oder um herumzustöbern und es zu untersuchen, aber in Bezug auf das Fädeln für die Vorteile einer tatsächlichen Multithread-Anwendung sind die beiden oben genannten Anwendungsfälle das stimmt so ziemlich.

DeadMG
quelle
2
+1 für Reaktionsfähigkeit: Eine der Anforderungen für mein Hauptprojekt ist, keinen Ladebildschirm zu haben, kontinuierliche Erfahrung für maximales Eintauchen. Das ist ein guter Punkt.
Klaim
1
+1 genau, warum ich in einem Puzzlespiel mehrere Threads verwendet habe: schwere Arbeit (Überprüfung von Millionen von Kombinationen), ohne die Benutzeroberfläche einzufrieren.
Asche999
"Kein Ladebildschirm, kontinuierliche Erfahrung" - Sie müssen Ihre gesamte Architektur auf dieser Idee aufbauen. Dies ist eine Pause vom Design "Laden Sie eine Ebene und spielen Sie darin, dann laden Sie eine andere Ebene", das die meisten kleinen Motoren verwenden.
Patrick Hughes
14

Wenn Ihr Spiel remote hardwareintensiv sein soll, benötigen Sie Threads, um mit der gesamten modernen Hardware fertig zu werden. Zukünftige CPUs, die in den nächsten ein oder zwei Jahren herauskommen, beginnen, mindestens 4 Kerne und bis zu 16 Kerne für Enthusiasten- / Leistungsmärkte zu verwenden. Wenn Sie überhaupt Multithreading ausführen, sollten Sie auf jeden Fall eine aufgabenorientierte Architektur erstellen, da jedes andere Threading-Modell für zukunftsweisende Spiel-Engines von Natur aus fehlerhaft ist.

Denken Sie jetzt daran, dass mit "Aufgaben" "Jobs" und nicht "separate Threads für verschiedene Engine-Subsysteme" gemeint sind. Sie möchten absolut nichts wie einen Grafik-Thread, einen Physik-Thread, einen KI-Thread usw. tun. Das skaliert nicht über eine winzige Handvoll Kerne hinaus und bringt Ihnen ohnehin keine echte Parallelität. Die Physik sollte nicht mehr als ein Update pro KI-Update ausführen (Sie möchten, dass Ihre KI auf Physikereignisse reagieren kann), und Grafiken müssen fast nichts Neues gerendert werden, wenn die Physik nicht ausgeführt wurde. Daher wird jedes Subsystem natürlich in einer Sequenz ausgeführt Auftrag. Du ziehst nicht

Was Sie tun möchten, ist dies. Erstellen Sie eine Garnrolle. Führen Sie Ihre Spielschleife mit der klassischen Folge von Subsystem-Updates aus. Trennen Sie jedoch für jedes Subsystem die Arbeitslast in verschiedene trennbare Stapel und verteilen Sie diese auf den Thread-Pool. Warten Sie, bis alle Jobs abgeschlossen sind, bevor Sie den nächsten Status der Spielaktualisierungsschleife ausführen. Einige Subsysteme können mehrere Unterstufen haben. Beispielsweise können Grafiken eine Reihe von Jobs zum Ausmerzen und dann eine zweite Reihe von Jobs zum Erstellen der Renderwarteschlange ausgeben. Dieser Ansatz vermeidet das Synchronisationsproblem des ersten Ansatzes, skaliert auf eine viel größere Anzahl von Kernen und ist offen gesagt einfacher zu codieren, zu debuggen und zu warten.

Sean Middleditch
quelle
Vielen Dank für die Ratschläge zum Bau dieser Art von Motor, aber ich bin bereits gut dokumentiert, so dass es nicht notwendig war. Es ist eher die Frage "Wann lohnt es sich?", Die ich stelle. Ich denke, es ist immer noch eine gute Sache, diese Informationen für diejenigen zu zeigen, die es nicht wissen, also entfernen Sie sie nicht :)
Klaim
Klingt sehr vernünftig
Den
Ähnlich wie bei Apple gibt es kein Zurück mehr, wenn Sie sich erst einmal an Jobs gewöhnt haben =) Dies ist ein guter Architekturvorschlag für jede neue Engine.
Patrick Hughes
@ SeanMiddleditch Ändern Sie nicht einfach die Granularität des Threadings auf diese Weise? Anstelle nur eines Threads pro System (was in der Tat nicht so skalierbar ist, sind einige Systeme viel leistungshungriger als andere, und es können auch viel mehr Systeme mit verfügbaren Kernen verfügbar sein) führen Sie möglicherweise mehrere Threads pro System aus. Sie optimieren also die Granularität der Daten, auf die jeder Thread einwirkt. Ich bin mir nicht sicher, wie das ausgehen wird, aber ich habe auch nichts Besseres hinzuzufügen. Halten Sie diese Meinung nach 7 Jahren noch? Vielen Dank.
Nikos
1
@ Nik-Lz: Nein, denn "mehrere Threads pro System" bedeutet, dass Sie wissen, was die Threads pro System sein sollten. Sollten Sie 2 Threads zum Rendern und 3 für die Physik haben? Wie viele für AI? Würde das nicht alles von der spezifischen Szene abhängen und sogar davon, wohin der Spieler in der Szene schaut und was im Moment los ist? Ein Jobsystem lässt sich leicht dynamisch auf die unmittelbaren Anforderungen skalieren, anstatt einen ganzen Thread an ein Subsystem zu binden, das es derzeit möglicherweise nicht benötigt.
Sean Middleditch
0

Beim Multithreading gibt es zwei Verwendungszwecke: Zum einen zur Verbesserung der Programmleistung und zum anderen zum Ausführen des Programms, wenn ein großer Prozess ausgeführt wird. Wenn Sie beispielsweise versuchen, einige Daten zu laden, ist es störend, wenn Ihr Programm auflegt. Sie können daher einen anderen Thread zum Laden verwenden und den Haupt-Thread frei halten, um die Hauptschleife fortzusetzen. Die Verbesserung der Leistung mithilfe von Threads ist dagegen sehr schwierig. da man normalerweise alles in einem linearen prozess macht und das ist etwas im gegensatz zur parallelverarbeitung. und Sie müssen immer Ihren Hauptthread für grafische Geräteaktualisierungen verwenden, was es noch schwieriger macht, Aufgaben zwischen Threads aufzuteilen.

Ali1S232
quelle
1
Einfädeln ist nicht schwer. :) Es ist nur etwas, was viele Leute nie richtig lernen, deshalb denken sie, dass es aufgrund von Unerfahrenheit schwierig ist. Insbesondere in Spielen sind viele der Algorithmen und Kerndatenstrukturen mit mehreren Threads recht einfach zu handhaben. Beispielsweise können Physik-Updates in Objektinseln unterteilt und jeweils zur Integration und Auflösung an einen anderen Thread ausgelagert werden. Ebenso ist das Ausmerzen von Objekten für Grafiken so gut wie frei von Konflikten beim Lesen / Schreiben-Datenzugriff.
Sean Middleditch
@seanmiddleditch Ich habe nicht gesagt, dass die Verwendung von Threads selbst eine schwierige Sache ist, aber es ist schwierig, Threads effektiv zu verwenden und sie auszugleichen, um dieselbe Last zu teilen.
Ali1S232