DIE KLINGEL

Es gibt diejenigen, die diese Nachrichten vor Ihnen lesen.
Abonnieren Sie, um die neuesten Artikel zu erhalten.
Email
Name
Nachname
Wie willst du The Bell lesen?
Kein Spam

Für die Informationsindustrie fiel der Beginn des 21. Jahrhunderts mit Verschiebungen zusammen, die als "tektonisch" bezeichnet werden können. Zu den Zeichen einer neuen Ära gehören die Verwendung von serviceorientierten Architekturen (SOA), Clusterkonfigurationen und vielem mehr, einschließlich Multi-Core-Prozessoren. Der Hauptgrund für das Geschehen ist natürlich die Entwicklung der Halbleiterphysik, die zu einer Zunahme der Anzahl logischer Elemente pro Flächeneinheit führte, wobei das Gesetz von Gordon Moore befolgt wurde. Die Anzahl der Transistoren auf einem Chip liegt bereits bei Hunderten von Millionen und wird bald die Milliarden-Dollar-Marke überschreiten, wodurch sich unweigerlich die Wirkung des bekannten Gesetzes der Dialektik manifestiert, das das Verhältnis von quantitativen und qualitativen Veränderungen postuliert. Unter den veränderten Bedingungen tritt eine neue Kategorie in den Vordergrund - komplexitätund Systeme werden sowohl auf Mikroebene (Prozessoren) als auch auf Makroebene (Unternehmensinformationssysteme) komplex.

Bis zu einem gewissen Grad kann das, was in der modernen Computerwelt geschieht, mit dem evolutionären Übergang verglichen werden, der vor Millionen von Jahren stattfand, als mehrzellige Organismen auftauchten. Zu diesem Zeitpunkt hatte die Komplexität einer Zelle eine bestimmte Grenze erreicht, und die nachfolgende Entwicklung folgte dem Weg der Entwicklung der infrastrukturellen Komplexität. Gleiches gilt für Computersysteme: Die Komplexität eines Prozessorkerns sowie die monolithische Architektur von Unternehmensinformationssystemen haben ein bestimmtes Maximum erreicht. Auf Makroebene gibt es nun einen Übergang von monolithischen Systemen zu Komponenten (oder aus Diensten zusammengesetzten), und die Aufmerksamkeit der Entwickler konzentriert sich auf die Infrastruktursoftware der mittleren Schicht, und auf Mikroebene erscheinen neue Prozessorarchitekturen.

Im wahrsten Sinne des Wortes verlor das Konzept der Komplexität in jüngster Zeit seine allgemein verwendete Bedeutung und wurde zu einem unabhängigen Faktor. In dieser Hinsicht ist die Komplexität noch nicht vollständig verstanden, und die Einstellung dazu ist nicht vollständig definiert, obwohl es seltsamerweise seit fast 50 Jahren eine separate wissenschaftliche Disziplin gibt, die als „Theorie komplexer Systeme“ bezeichnet wird. (Denken Sie daran, dass "komplex" theoretisch ein System ist, dessen einzelne Komponenten nichtlinear kombiniert werden. Ein solches System ist nicht nur eine Summe von Komponenten, wie dies in linearen Systemen der Fall ist.) Man kann sich nur wundern, dass die Systemtheorie von diesen Spezialisten und Unternehmen noch nicht akzeptiert wurde. deren Tätigkeit führt sie zur Schaffung dieser komplexen Systeme mittels Informationstechnologie.

Der Engpass der von Neumann-Architektur

Auf Mikroebene kann ein Analogon zum Übergang von einzelligen Organismen zu mehrzelligen Organismen der Übergang von Einkernprozessoren zu Mehrkernprozessoren (Chip MultiProcessors, CMP) sein. CMP bietet eine Möglichkeit, die inhärente Schwäche moderner Prozessoren zu überwinden - den Engpass der von Neumann-Architektur.

Dies sagte John Backus bei der Turing-Preisverleihung 1977: „Was ist ein von Neumann-Computer? Als John von Neumann und andere vor 30 Jahren ihre ursprüngliche Architektur vorschlugen, schien die Idee elegant, praktisch und vereinfacht zu sein, um eine Vielzahl von technischen und Programmierproblemen zu lösen. Und obwohl sich die Bedingungen zum Zeitpunkt der Veröffentlichung in der Vergangenheit radikal geändert haben, identifizieren wir unser Verständnis von Computern mit diesem alten Konzept. In seiner einfachsten Form besteht ein von Neumann-Computer aus drei Teilen: zentralprozessor (CPU oder CPU), Speicher und der sie verbindende Kanal, der zum Datenaustausch zwischen CPU und Speicher dient, und zwar in kleinen Teilen (jeweils nur ein Wort). Ich schlage vor, diesen Kanal "von Neumanns Engpass" zu nennen. Sicherlich sollte es eine weniger primitive Lösung geben, als eine große Datenmenge durch einen Engpass zu pumpen. Ein solcher Kanal schafft nicht nur ein Problem für den Verkehr, sondern ist auch ein "intellektueller Engpass", der Programmierern "Wort für Wort" das Denken auferlegt und sie daran hindert, in höheren konzeptuellen Kategorien zu denken.

Backus war am bekanntesten für die Erstellung der Fortran-Sprache Mitte der 50er Jahre, die in den nächsten Jahrzehnten das beliebteste Werkzeug für die Erstellung von Computerprogrammen war. Aber später erkannte Backus offenbar zutiefst seine Schwächen und erkannte, dass er "die von Neumann-Sprache" aller Hochsprachen entwickelt hatte. Daher war das Hauptpathos seiner Kritik in erster Linie auf unvollständige Programmiermethoden gerichtet.

Seit Backus seine Rede hielt, gab es bedeutende Fortschritte in den Bereichen Programmierung, funktionale und objektorientierte Technologien, und mit ihrer Hilfe konnte der von Backus als "intellektueller von Neumann-Engpass" bezeichnete Engpass überwunden werden. Die architektonische Grundursache für dieses Phänomen, die angeborene Erkrankung des Kanals zwischen Speicher und Prozessor - seine begrenzte Bandbreite - ist jedoch trotz technologischer Fortschritte in den letzten 30 Jahren nicht verschwunden. Im Laufe der Jahre ist dieses Problem stetig gewachsen, da die Speichergeschwindigkeit viel langsamer als die Prozessorleistung wächst und sich die Lücke zwischen ihnen vergrößert.

Von Neumanns Computerarchitektur ist nicht die einzig mögliche. Unter dem Gesichtspunkt der Organisation des Befehlsaustauschs zwischen Prozessor und Speicher können alle Computer in vier Klassen unterteilt werden:

  • SISD (Single Instruction Single Data) - "ein Befehlsstrom, ein Datenstrom" ";
  • SIMD (Single Instruction Multiply Data) - ein Befehlsstrom, viele Datenströme;
  • MISD (Multiple Instruction Single Data) - viele Befehlsströme, ein Datenstrom;
  • MIMD (Multiple Instruction Multiple Data) - viele Befehlsströme, viele Datenströme.

Diese Klassifizierung zeigt, dass die von Neumann-Maschine ein Sonderfall ist, der in die Kategorie SISD fällt. Mögliche Verbesserungen innerhalb der SISD-Architektur beschränken sich auf die Einbeziehung von Pipelines und anderen zusätzlichen Funktionsknoten sowie auf die Verwendung verschiedener Caching-Methoden. Zwei weitere Kategorien von Architekturen (SIMD, einschließlich Vektorprozessoren, und Pipeline-Architekturen MISD) wurden in mehreren Projekten implementiert, sind jedoch nicht zum Mainstream geworden. Wenn wir im Rahmen dieser Klassifizierung bleiben, besteht die einzige Möglichkeit, die Engpassbeschränkungen zu überwinden, in der Entwicklung von MIMD-Klassenarchitekturen. In ihrem Rahmen gibt es viele Ansätze: Es können verschiedene Parallel- und Cluster-Architekturen sowie Multithread-Prozessoren sein.

Vor einigen Jahren wurden aufgrund technologischer Einschränkungen alle Multithread-Prozessoren auf der Basis eines einzigen Kerns gebaut, und ein solches Multithreading wurde als "simultan" bezeichnet. Simultanes MultiThreading (SMT)... Und mit dem Aufkommen von Multi-Core-Prozessoren erschien eine alternative Art von Multithreading - Chip MultiProcessors (CMP).

Merkmale von Multithread-Prozessoren

Der Übergang von einfachen Single-Thread-Prozessoren zu logisch komplexeren Multi-Thread-Prozessoren beinhaltet die Überwindung spezifischer Schwierigkeiten, die zuvor noch nicht aufgetreten sind. Der Betrieb eines Geräts, bei dem der Ausführungsprozess in Agenten oder Threads (Threads) unterteilt ist, weist zwei Funktionen auf:

  • unbestimmtheitsprinzip... In einer Multithread-Anwendung wird ein Prozess in interagierende Agententhreads ohne vorgegebene Bestimmtheit unterteilt.
  • unschärferelation... Wie genau Ressourcen zwischen Agententhreads zugewiesen werden, ist ebenfalls im Voraus unbekannt.

Aufgrund dieser Merkmale unterscheidet sich der Betrieb von Multithread-Prozessoren grundlegend von deterministischen Berechnungen nach dem von Neumann-Schema. In diesem Fall kann der aktuelle Zustand des Prozesses nicht als lineare Funktion des vorherigen Zustands und der Eingabedaten definiert werden, obwohl jeder der Prozesse als von Neumann-Mikromaschine betrachtet werden kann. (In einer Anwendung auf das Verhalten von Threads können Sie sogar den in der Quantenphysik verwendeten Begriff "Fremdheit" verwenden.) Das Vorhandensein dieser Merkmale bringt einen Multithread-Prozessor näher an das Konzept eines komplexen Systems heran, aber aus rein praktischer Sicht ist klar, dass es auf der Ebene der Prozessausführung keinen Nichtdeterminismus gibt Unsicherheit, und noch mehr über Fremdheit und Sprache kann nicht sein. Ein korrekt ausgeführtes Programm kann nicht seltsam sein.

In seiner allgemeinsten Form besteht ein Multithread-Prozessor aus zwei Arten von Grundelementen. Der erste Typ ist eine Ressource, die die Ausführung des Streams unterstützt, der als Mutex (aus gegenseitigem Ausschluss) bezeichnet wird, und der zweite ist Ereignisse. Wie dieser oder jener Mutex physisch implementiert wird, hängt vom gewählten Schema ab - SMT oder CMP. In jedem Fall wird die Ausführung des Prozesses auf die Tatsache reduziert, dass der nächste Thread den Mutex für die Dauer seiner Ausführung erfasst und ihn dann freigibt. Wenn der Mutex von einem Thread belegt ist, kann der zweite Thread ihn nicht abrufen. Das spezielle Verfahren zum Delegieren des Mutex-Eigentums von einem Thread an einen anderen kann zufällig sein. Dies hängt von der Implementierung der Steuerung ab, beispielsweise in einem bestimmten Betriebssystem. In jedem Fall sollte die Steuerung so strukturiert sein, dass Ressourcen, die aus Mutex bestehen, korrekt zugewiesen werden und der Effekt der Unsicherheit unterdrückt wird.

Ereignisse sind Objekte (Ereignisse), die eine Änderung der externen Umgebung signalisieren. Sie können sich in die Warteschleife stellen, bis ein anderes Ereignis eintritt, oder ihren Status einem anderen Ereignis melden. Auf diese Weise können Ereignisse miteinander interagieren, und gleichzeitig muss die Datenkontinuität zwischen Ereignissen sichergestellt werden. Der anstehende Agent muss darüber informiert werden, dass die Daten dafür bereit sind. Und wie bei der Verteilung von Mutex muss der Effekt der Unsicherheit unterdrückt werden, so muss bei der Arbeit mit Ereignissen der Effekt der Unsicherheit unterdrückt werden. Zum ersten Mal wurde das SMT-Schema in den Compaq Alpha 21464-Prozessoren sowie in Intel Xeon MP und Itanium implementiert.

CMP ist logischerweise einfacher: Hier wird Parallelität durch die Tatsache bereitgestellt, dass jeder der Threads von seinem eigenen Kern verarbeitet wird. Wenn eine Anwendung jedoch nicht mit einem Thread versehen werden kann, wird sie (ohne besondere Maßnahmen) von einem Kern verarbeitet, und in diesem Fall wird die Gesamtleistung des Prozessors durch die Geschwindigkeit eines Kerns begrenzt. Auf den ersten Blick ist ein auf dem SMT-Schema basierender Prozessor flexibler, weshalb dieses Schema vorzuziehen ist. Diese Aussage gilt jedoch nur für eine geringe Transistordichte. Wenn die Frequenz in Megahertz gemessen wird und sich die Anzahl der Transistoren im Kristall einer Milliarde nähert und die Verzögerungen bei der Signalübertragung länger als die Schaltzeit werden, profitiert die CMP-Mikroarchitektur, in der die zugehörigen Rechenelemente lokalisiert sind.

Die physikalische Parallelisierung macht CMP jedoch für sequentielle Berechnungen nicht sehr effizient. Um diesen Nachteil zu überwinden, wird ein Ansatz namens "Speculative Multithreading" verwendet. Im Russischen hat das Wort "spekulativ" eine negative Konnotation, daher werden wir dieses Multithreading "bedingt" nennen. Dieser Ansatz setzt Hardware- oder Softwareunterstützung für die Aufteilung einer sequentiellen Anwendung in bedingte Threads, die Koordination ihrer Ausführung und die Integration der Ergebnisse in den Speicher voraus.

CMP-Evolution

Die ersten Mainstream-CMPs waren auf den Servermarkt ausgerichtet. Unabhängig vom Anbieter handelte es sich im Wesentlichen um zwei unabhängige superskalare Prozessoren auf einem Substrat. Die Hauptmotivation für diese Designs besteht darin, das Volumen zu reduzieren, damit mehr Prozessoren in einem einzigen Design „verpackt“ werden können, wodurch die Leistungsdichte pro Volumeneinheit erhöht wird (was für heutige Rechenzentren von entscheidender Bedeutung ist). Auf der Ebene des gemeinsamen Systems werden dann einige zusätzliche Einsparungen erzielt, da die Prozessoren auf demselben Chip gemeinsam genutzte Systemressourcen wie Hverwenden. Normalerweise gibt es nur eine gemeinsame Systemschnittstelle zwischen benachbarten Prozessoren ( feige. 1, b).

Entschuldiger für die Verwendung von CMP-Prozessoren rechtfertigen die weitere (über zwei) Zunahme der Anzahl von Kernen durch die Besonderheiten der Serverlast, die diesen Computertyp von eingebetteten oder massiven Computersystemen unterscheidet. Vom Server ist eine hohe Gesamtleistung erforderlich, aber die Latenz eines einzelnen Aufrufs des Systems ist nicht so kritisch. In einem einfachen Beispiel bemerkt ein Benutzer möglicherweise nicht die Millisekundenverzögerung beim Erscheinen einer aktualisierten Webseite, reagiert jedoch sehr empfindlich auf Serverüberlastungen, die zu Betriebsstörungen führen können.

Die spezifische Arbeitslast bietet CMP-Prozessoren einen weiteren spürbaren Vorteil. Wenn Sie beispielsweise einen Single-Core-Prozessor durch einen Dual-Core-Prozessor ersetzen, können Sie die Taktfrequenz bei gleicher Leistung halbieren. In diesem Fall kann sich theoretisch die Verarbeitungszeit für eine einzelne Anforderung verdoppeln, aber da die physikalische Trennung von Strömen die Engpassbeschränkung der von Neumann-Architektur verringert, beträgt die Gesamtverzögerung viel weniger als das Zweifache. Mit einer geringeren Frequenz und Komplexität eines Kerns wird der Stromverbrauch erheblich reduziert, und mit zunehmender Anzahl von Kernen werden die obigen Argumente für CMP nur noch stärker. Daher besteht der nächste logische Schritt darin, mehrere Kerne zu sammeln und sie mit einem gemeinsamen Cache-Speicher zu kombinieren, beispielsweise wie im Hydra-Projekt (Abbildung 1, c). Und dann können Sie die Kerne komplizieren und zu Multithreads machen, was im Niagara-Projekt implementiert wurde (Abbildung 1, d).

Die Komplexität von Prozessoren hat eine weitere wichtige Manifestation. Das Entwerfen eines Produkts mit Milliarden von Komponenten wird - trotz des Einsatzes von Automatisierung - zu einer zunehmend arbeitsintensiven Aufgabe. Es ist bezeichnend, dass wir mehr als ein Jahrzehnt der „Feinabstimmung“ der IA-64-Architektur erleben. Das Entwerfen eines CMP-Prozessors ist viel einfacher: Wenn ein gut entwickelter Kern vorhanden ist, kann er in den erforderlichen Mengen repliziert werden, und das Entwerfen beschränkt sich auf die Erstellung der internen Infrastruktur des Kristalls. Darüber hinaus vereinfacht die Einheitlichkeit der Kerne das Design von Motherboards, was auf die Skalierung hinausläuft, und letztendlich ändert sich die Leistung der E / A-Subsysteme.

Trotz der obigen Argumente gibt es immer noch keine ausreichende Grundlage für eine eindeutige Aussage über die Vorteile von CMP gegenüber SMT. Die Erfahrung mit der Erstellung von Prozessoren zur Implementierung von SMT ist viel größer: Seit Mitte der 1980er Jahre wurden mehrere Dutzend experimentelle Produkte und mehrere serielle Prozessoren erstellt. Die Geschichte der CPM-Entwicklung ist noch kurz: Abgesehen von der TMS 320C8x-Familie spezialisierter Signalprozessoren von Texas Instruments war Hydra das erste erfolgreiche Projekt, das an der Stanford University durchgeführt wurde. Unter den universitären Forschungsprojekten zum Bau von CMP-Prozessoren sind drei weitere bekannt - Wisconsin Multiscalar, Carnegie-Mellon Stampede und MIT M-machine.

Hydra Mikroprozessor

Der Hydra-Kristall besteht aus vier Prozessorkernen, die auf der bekannten MIPS RISC-Architektur basieren. Jeder Kern verfügt über einen Anweisungscache und einen Datencache, und alle Kerne werden zu einem gemeinsamen L2-Cache zusammengefasst. Prozessoren führen den normalen MIPS-Befehlssatz sowie Store Conditional- oder SC-Befehle zum Implementieren von Synchronisationsprimitiven aus. Prozessoren und L2-Cache sind durch Lese- / Schreibbusse verbunden, und zusätzlich gibt es Hilfsadressen- und Steuerbusse. Alle diese Busse sind virtuell, dh sie werden logisch durch verdrahtete Busse dargestellt und mithilfe von Repeatern und Puffern physisch in viele Segmente unterteilt, wodurch die Geschwindigkeit der Kerne erhöht werden kann.

Der Lese- / Schreibbus spielt die Rolle eines Systembusses. Aufgrund seiner Lage im Kristall hat es ausreichend durchsatz um den Austausch mit dem Cache-Speicher in einem Zyklus sicherzustellen. Es ist schwierig, solche Austauschleistungsindikatoren selbst in den teuersten herkömmlichen Multiprozessorarchitekturen zu erreichen, da die Anzahl der externen Prozessorkontakte physikalisch begrenzt ist. Effiziente Cache-Busse verhindern den Engpass zwischen Kernen und Speicher.

Das Testen der Hydra unter stark parallelen Workloads in typischen Web- und Serveranwendungen hat gezeigt, dass vier Kerne drei- bis 3,8-mal schneller arbeiten als ein einzelner Kern, der nahezu linear ist. Dies gibt Anlass zu der Annahme, dass Prozessoren dieses Typs recht erfolgreich in Anwendungen "passen", die Server mit SMP-Architektur verwenden. Es ist jedoch klar, dass der Prozessor mit sequentiellen Anwendungen sehr effizient arbeiten muss. Eine der wichtigsten Aufgaben ist daher die Implementierung des bedingten Multithreading. In Hydra ist es in Hardware implementiert, und die Wahl dieses Ansatzes wird durch die Tatsache gerechtfertigt, dass für die Programmierung paralleler Anwendungen keine zusätzlichen Kosten erforderlich sind.

Bedingtes Multithreading basiert auf der Aufteilung einer Folge von Programmbefehlen in Threads, die parallel ausgeführt werden können. Natürlich kann es eine logische Interdependenz zwischen solchen Threads geben, und ein spezieller Synchronisationsmechanismus ist in den Prozessor eingebaut, um sie anzupassen. Das Wesentliche seiner Arbeit besteht darin, dass die Ausführung eines solchen Threads ausgesetzt wird, wenn für einen Thread Daten von einem parallelen Thread erforderlich sind und diese noch nicht bereit sind. Tatsächlich manifestieren sich hier die oben diskutierten Elemente des Nichtdeterminismus. Der Synchronisationsprozess ist ziemlich komplex, da alle möglichen Abhängigkeiten zwischen Threads und Synchronisationsbedingungen ermittelt werden müssen. Durch die bedingte Synchronisation können Programme ohne vorherige Kenntnis ihrer Eigenschaften parallelisiert werden. Es ist wichtig, dass der Synchronisationsmechanismus dynamisch ist. Er funktioniert ohne die Intervention eines Programmierers oder Compilers, der Anwendungen nur statisch in Threads aufteilen kann. Tests des Modells, die auf verschiedenen Tests basieren, haben gezeigt, dass die Mittel des bedingten Multithreading die Prozessorleistung um ein Vielfaches erhöhen können. Je expliziter die Parallelität des Tests ist, desto niedriger ist dieser Wert.

Afara wurde im Jahr 2000 in einem sehr geheimen Umfeld gegründet. Die Gründer sind Professor Kunle Olukotun von der Stanford University und der renommierte Prozessordesigner Les Cohn, der bei Intel und Sun Microsystems gearbeitet hat. Cohn war einer der Autoren der i860- und i960-RISC-Prozessoren im ersten dieser Unternehmen und UltraSPARC-I im zweiten. Unter seiner Führung wurde Hydra für SPARC-Prozessorkerne neu gestaltet. Im Jahr 2002 wurde Afara von Sun Microsystems gekauft, und damit endete die Hydra-Geschichte und die Niagara-Geschichte begann.

Niagara - "Fusion" von MAJC und Hydra

Der UltraSPARC T1-Prozessor, besser bekannt als Niagara, hat zwei Hauptvorgänger, den Hydra und den MAJC.

Mitte der neunziger Jahre versuchte Sun Microsystems aufgrund der Begeisterung für spezialisierte Java-Prozessoren, einen VLIW-Prozessor (Very Long Instruction Word) zu erstellen. Diese Initiative wurde MAJC (Microprocessor Architecture for Java Computing) genannt. Wie bei anderen Projekten, die zu dieser Zeit gestartet wurden (Intel IA-64 Itanium), bestand in diesem Fall die Aufgabe darin, einige der komplexesten Vorgänge auf den Compiler zu übertragen. Die freigegebene Transistorlogik kann verwendet werden, um effizientere Funktionseinheiten zu erstellen, um einen effizienten Austausch von Anweisungen und Daten zwischen der CPU, dem Cache-Speicher und dem Hauptspeicher sicherzustellen. Damit wurde von Neumanns Engpass überwunden.

MAJC unterschied sich von den meisten Prozessoren durch das Fehlen spezialisierter Co-Prozessoren (Subprozessoren), die üblicherweise als Funktionseinheiten bezeichnet werden, die für Operationen mit Ganzzahlen, Gleitkommazahlen und Multimediadaten ausgelegt sind. Alles drin funktionsgeräte waren die gleichen, die in der Lage waren, alle Operationen auszuführen, was einerseits die Effizienz einzelner Operationen verringerte, andererseits aber die Auslastung des gesamten Prozessors erhöhte.

Niagara verkörpert das Beste aus zwei alternativen Ansätzen für Multithreading - SMT und CMP. Auf den ersten Blick sieht es der Hydra sehr ähnlich, aber die Hydra kann eher als "Mock" von Niagara bezeichnet werden. Zusätzlich zu der Tatsache, dass letzterer doppelt so viele Kerne hat, kann jeder von ihnen vier Threads verarbeiten.

Der Niagara-Prozessor bietet Hardware-Unterstützung für die Ausführung von 32 Threads, die in acht Gruppen (jeweils vier Threads) unterteilt sind. Jede Gruppe hat ihre eigene SPARC-Pipeline ( abb. 2). Er ist prozessorkerngebaut nach der SPARC V9 Architektur. Jede SPARC-Pipeline enthält einen Cache der ersten Ebene für Befehle und Daten. Zusammen teilen sich 32 Threads einen 3 MB L2-Cache, der in vier Bänke unterteilt ist. Der Switch verbindet acht Kerne, L2-Cache-Bänke und andere zugewiesene CPU-Ressourcen und unterstützt eine Übertragungsrate von 200 GB / s. Darüber hinaus enthält der Switch einen Port für E / A-Systeme und Kanäle zum Speicher des DDR2-DRAM-Typs, der einen Wechselkurs von 20 GB / s bietet. Die maximale Speicherkapazität beträgt bis zu 128 GB.

Das Niagara-Projekt konzentriert sich auf das Solaris-Betriebssystem, sodass alle unter Solaris ausgeführten Anwendungen ohne Änderungen auf dem neuen Prozessor ausgeführt werden können. Die Anwendungssoftware interpretiert Niagara als 32 diskrete Prozessoren.

Zellprojekt

Ein eigener Ansatz zur Erstellung von Multi-Core-Prozessoren wurde von der IBM Corporation vorgeschlagen, deren Cell-Projekt als "heterogener Chip-Multiprozessor" bezeichnet wurde. Die Zellenarchitektur wird auch als Cell Broadband Engine Architecture (CBEA) bezeichnet. Der Cell-Multiprozessor besteht aus einem IBM 64-Bit-Power Architecture-Kern und acht spezialisierten Coprozessoren, die ein Ein-Befehl-Viele-Daten-Schema implementieren. IBM nennt diese Architektur die Synergistic Processor Unit (SPU). Es kann erfolgreich eingesetzt werden, wenn große Datenströme verarbeitet werden müssen, beispielsweise in der Kryptographie, in verschiedenen multimedialen und wissenschaftlichen Anwendungen wie der schnellen Fourier-Transformation oder Matrixoperationen. Die Cell-Architektur wurde von IBM Research und Kollegen der IBM Systems Technology Group, Sony und Toshiba, erstellt und wird voraussichtlich die erste Anwendung in rechenintensiven Multimedia-Geräten sein.

Der Kern der Synergistic Processor Unit ist der ISA-Befehlssatz (Instruction Set Architecture). Die Befehle sind 32 Bit lang und an drei Operanden adressiert, die sich im Registerpool befinden, der aus 128 Registern mit jeweils 128 Bit besteht.

In Zukunft wird die Verwendung von Cell nicht auf Spielesysteme beschränkt sein. Als nächstes kommt das Fernsehen hochauflösend, Heimserver und sogar Supercomputer.

Literatur
  1. Leonid Chernyak. Überarbeitung der ersten Prinzipien - das Ende der Stagnation? // Systeme öffnen. - 2003, Nr. 5.
  2. Mikhail Kuzminsky. Multithread-Architektur von Mikroprozessoren // Offene Systeme. - 2002, Nr. 1.
  3. Rajat A Dua, Bhushan Lokhande. Eine vergleichende Studie von SMT- und CMP-Multiprozessoren. - -

Einführung. Die Computertechnologie entwickelt sich rasant. Computergeräte werden immer leistungsfähiger, kompakter und bequemer, aber in letzter Zeit ist die Verbesserung der Geräteleistung zu einem großen Problem geworden. 1965 kam Gordon Moore (einer der Gründer von Intel) zu dem Schluss, dass sich "die Anzahl der auf einem integrierten Schaltkreischip platzierten Transistoren alle 24 Monate verdoppelt".

Die ersten Entwicklungen auf dem Gebiet der Schaffung von Multiprozessorsystemen begannen in den 70er Jahren. Lange Zeit stieg die Leistung herkömmlicher Single-Core-Prozessoren durch Erhöhen der Taktfrequenz (bis zu 80% der Leistung wurden nur durch die Taktfrequenz bestimmt) bei gleichzeitiger Erhöhung der Anzahl der Transistoren auf dem Chip. Die Grundgesetze der Physik stoppten diesen Prozess: Die Chips begannen sich zu überhitzen, die technologischen näherten sich der Größe von Siliziumatomen. All diese Faktoren haben dazu geführt, dass:

  • erhöhte Leckströme, was zu einer erhöhten Wärmeerzeugung und einem erhöhten Stromverbrauch führt.
  • der Prozessor ist viel "schneller" als der Speicher geworden. Die Leistung ging aufgrund der Latenz beim Zugriff zurück arbeitsspeicher und Laden von Daten in den Cache.
  • es gibt so etwas wie "von Neumann-Engpass". Dies bedeutet die Ineffizienz der Prozessorarchitektur bei der Ausführung eines Programms.

Multiprozessorsysteme (als eine der Möglichkeiten zur Lösung des Problems) waren nicht weit verbreitet, da sie teure und schwierig herzustellende Multiprozessorsysteme erforderten. motherboards... Auf dieser Grundlage wurde die Produktivität auf andere Weise verbessert. Das Konzept des Multithreading erwies sich als effektiv - die gleichzeitige Verarbeitung mehrerer Befehlsströme.

Hyper-Threading-Technologie (HTT) oder Hyper-Threading-Technologie, mit der ein Prozessor mehrere Threads auf einem einzigen Kern ausführen kann. Es ist HTT, das nach Ansicht vieler Experten zu einer Voraussetzung für die Erstellung von Multi-Core-Prozessoren geworden ist. Ein Prozessor, der mehrere Threads gleichzeitig ausführt, wird als Parallelität auf Thread-Ebene (TLP - Parallelität auf Thread-Ebene) bezeichnet.

Um das Potenzial eines Multi-Core-Prozessors auszuschöpfen, muss ein ausführbares Programm alle Rechenkerne verwenden, was nicht immer erreichbar ist. Alte sequentielle Programme, die nur einen Kern verwenden können, werden auf der neuen Generation von Prozessoren nicht mehr schneller ausgeführt, sodass Programmierer zunehmend an der Entwicklung neuer Mikroprozessoren beteiligt sind.

1. Allgemeine Konzepte

Architektur im weitesten Sinne ist eine Beschreibung eines komplexen Systems, das aus vielen Elementen besteht.

Während des Entwicklungsprozesses entwickeln sich Halbleiterstrukturen (Mikroschaltungen). Daher ändern sich die Prinzipien des Bauens von Prozessoren, die Anzahl der in ihrer Zusammensetzung enthaltenen Elemente und die Art und Weise, wie ihre Wechselwirkungen organisiert sind, ständig. Daher werden CPUs mit denselben grundlegenden Entwurfsprinzipien üblicherweise als Prozessoren derselben Architektur bezeichnet. Und solche Prinzipien selbst werden als Prozessorarchitektur (oder Mikroarchitektur) bezeichnet.

Ein Mikroprozessor (oder Prozessor) ist die Hauptkomponente eines Computers. Es verarbeitet Informationen, führt Programme aus und steuert andere Geräte im System. Wie schnell Programme ausgeführt werden, hängt von der Leistung des Prozessors ab.

Der Kern ist das Rückgrat eines jeden Mikroprozessors. Es besteht aus Millionen von Transistoren, die sich auf einem Siliziumchip befinden. Der Mikroprozessor ist in spezielle Zellen unterteilt, die als Register bezeichnet werden allgemeiner Zweck (ROZ) Die Arbeit des Prozessors besteht insgesamt darin, Befehle und Daten in einer bestimmten Reihenfolge aus dem Speicher abzurufen und auszuführen. Um die Leistung des PCs zu verbessern, ist der Mikroprozessor außerdem mit einem internen Cache-Speicher ausgestattet. Der Cache-Speicher ist der interne Speicher des Prozessors, der als Puffer verwendet wird (zum Schutz vor Unterbrechungen bei der Kommunikation mit dem RAM).

Intel-Prozessoren, die in IBM-kompatiblen PCs verwendet werden, verfügen über mehr als tausend Anweisungen und werden als Prozessoren mit erweitertem Befehlssatz bezeichnet - CISC-Prozessoren (CISC - Complex Instruction Set Computing).

1.1 Hochleistungsrechnen. Parallelität

Das Entwicklungstempo der Computertechnologie ist leicht nachzuvollziehen: von ENIAC (dem ersten elektronischen Digitalcomputer für den allgemeinen Gebrauch) mit einer Leistung von mehreren tausend Operationen pro Sekunde bis zum Supercomputer Tianhe-2 (1000 Billionen Gleitkommaoperationen pro Sekunde). Dies bedeutet, dass sich die Rechengeschwindigkeit in 60 Jahren um eine Billion Mal erhöht hat. Die Schaffung von Hochleistungsrechnersystemen ist eines der schwierigsten wissenschaftlichen und technischen Probleme. Angesichts der Geschwindigkeit der Berechnungen technische Mittel ist nur ein paar Millionen Mal gewachsen, die Gesamtgeschwindigkeit des Rechnens ist Billionen Mal gewachsen. Dieser Effekt wird durch die Verwendung von Parallelität in allen Phasen der Berechnung erreicht. Paralleles Rechnen erfordert die Suche nach einer rationalen Speicherzuordnung, zuverlässigen Methoden zur Informationsübertragung und der Koordinierung von Rechenprozessen.

1.2 Symmetrische Mehrfachverarbeitung

Symmetric Multiprocessing (abgekürzt SMP) oder Symmetric Multiprocessing ist eine spezielle Architektur von Multiprozessorsystemen, bei denen mehrere Prozessoren Zugriff auf gemeinsam genutzten Speicher haben. Dies ist eine sehr verbreitete Architektur, die in letzter Zeit weit verbreitet ist.

Bei Verwendung von SMP arbeiten mehrere Prozessoren gleichzeitig in einem Computer, jeder für sich. Ein SMP-System mit einem hochwertigen Betriebssystem verteilt Aufgaben rational auf die Prozessoren und sorgt für eine gleichmäßige Belastung. Es gibt jedoch ein Problem mit dem Speicherzugriff, da selbst Einzelprozessorsysteme relativ lange brauchen, um dies zu tun. Somit erfolgt der Zugriff auf RAM in SMP nacheinander: zuerst ein Prozessor, dann der zweite.

Aufgrund der oben aufgeführten Funktionen werden SMP-Systeme ausschließlich im wissenschaftlichen Bereich, in der Industrie, in der Wirtschaft und äußerst selten in Arbeitsbüros eingesetzt. Zusätzlich zu den hohen Kosten für die Hardware-Implementierung erfordern solche Systeme sehr teure und qualitativ hochwertige Software für Multithreading-Aufgaben. Regelmäßige Programme (Spiele, texteditoren) funktionieren in SMP-Systemen nicht effektiv, da sie diesen Grad an Parallelität nicht vorsehen. Wenn Sie ein Programm für ein SMP-System anpassen, wird es äußerst ineffizient, auf Einprozessorsystemen zu arbeiten, was dazu führt, dass mehrere Versionen desselben Programms für verschiedene Systeme erstellt werden müssen. Eine Ausnahme bildet beispielsweise das ABLETON LIVE-Programm (zum Erstellen von Musik und Vorbereiten von DJ-Sets), das Multiprozessorsysteme unterstützt. Wenn Sie ein reguläres Programm auf einem Multiprozessorsystem ausführen, wird es immer noch etwas schneller ausgeführt als auf einem Einprozessorsystem. Dies liegt an dem sogenannten Hardware-Interrupt (Stoppen des Programms zur Verarbeitung durch den Kernel), der auf einem anderen freien Prozessor ausgeführt wird.

Ein SMP-System (wie jedes andere, das auf parallelem Rechnen basiert) stellt erhöhte Anforderungen an einen Speicherparameter wie die Speicherbusbandbreite. Dies begrenzt häufig die Anzahl der Prozessoren im System (moderne SMP-Systeme arbeiten effizient bis zu 16 Prozessoren).

Da die Prozessoren über einen gemeinsamen Speicher verfügen, ist eine rationelle Verwendung und Datenkoordination erforderlich. In einem Multiprozessorsystem stellt sich heraus, dass mehrere Caches für eine gemeinsam genutzte Speicherressource funktionieren. Die Cache-Kohärenz ist eine Cache-Eigenschaft, die die Integrität der in einzelnen Caches für eine gemeinsam genutzte Ressource gespeicherten Daten sicherstellt. Dieses Konzept ist ein Sonderfall des Konzepts der Speicherkohärenz, bei dem mehrere Kerne Zugriff auf gemeinsam genutzten Speicher haben (es ist in modernen Multicore-Systemen allgegenwärtig). Wenn wir diese Konzepte allgemein beschreiben, sieht das Bild wie folgt aus: Derselbe Datenblock kann in verschiedene Caches geladen werden, in denen die Daten auf unterschiedliche Weise verarbeitet werden.

Wenn keine Datenänderungsbenachrichtigungen verwendet werden, tritt ein Fehler auf. Die Cache-Kohärenz soll solche Konflikte lösen und die Datenkonsistenz in Caches gewährleisten.

SMP-Systeme sind eine Untergruppe von MIMD (Multi-Instruction Multi Data) der Flynn-Klassifikation von Computersystemen (Professor an der Stanford University, Mitbegründer von Palyn Associates). Nach dieser Klassifizierung können fast alle Arten von Parallelsystemen als MIMD klassifiziert werden.

Die Unterteilung von Multiprozessorsystemen in Typen basiert auf der Unterteilung nach dem Prinzip der Speichernutzung. Dieser Ansatz ermöglichte die Unterscheidung zwischen den folgenden wichtigen Typen

multiprozessorsysteme - Multiprozessoren (Multiprozessorsysteme mit gemeinsam genutztem gemeinsam genutztem Speicher) und Multicomputer (Systeme mit separatem Speicher). Gemeinsame Daten, die beim parallelen Rechnen verwendet werden, erfordern eine Synchronisation. Die Aufgabe der Datensynchronisation ist eines der wichtigsten Probleme und ihre Lösung bei der Entwicklung von Multiprozessor und Multicore. Dementsprechend hat die erforderliche Software für Ingenieure und Programmierer Priorität. Die gemeinsame Nutzung von Daten kann durch physische Zuweisung von Speicher erfolgen. Dieser Ansatz wird als ungleichmäßiger Speicherzugriff (NUMA) bezeichnet.

Diese Systeme umfassen:

  • Systeme, bei denen nur einzelne Prozessor-Caches für die Datenpräsentation verwendet werden (Nur-Cache-Speicherarchitektur).
  • Systeme mit der Bereitstellung der Kohärenz lokaler Caches für verschiedene Prozessoren (Cache-kohärente NUMA).
  • Systeme mit gemeinsamem Zugriff auf einzelnen Prozessorspeicher ohne Hardware-Implementierung von nicht-Cache-kohärentem NUMA.

Die Vereinfachung des Problems der Erstellung von Multiprozessorsystemen wird durch die Verwendung eines verteilten gemeinsamen Speichers erreicht, aber dieses Verfahren führt zu einer signifikanten Erhöhung der Komplexität der parallelen Programmierung.

1.3 Gleichzeitiges Multithreading

Basierend auf all den oben genannten Nachteilen der symmetrischen Mehrfachverarbeitung ist es sinnvoll, andere Wege zur Verbesserung der Leistung zu entwickeln und zu entwickeln. Wenn Sie den Betrieb jedes einzelnen Transistors im Prozessor analysieren, können Sie auf eine sehr interessante Tatsache aufmerksam machen - bei der Durchführung der meisten Rechenoperationen sind weit entfernt alle Prozessorkomponenten beteiligt (nach jüngsten Studien etwa 30% aller Transistoren). Wenn der Prozessor beispielsweise eine einfache arithmetische Operation ausführt, befindet sich der größte Teil des Prozessors im Leerlauf und kann daher für andere Berechnungen verwendet werden. Wenn der Prozessor gerade reale Operationen ausführt, kann eine ganzzahlige arithmetische Operation in den freien Teil geladen werden. Um die Belastung des Prozessors zu erhöhen, können Sie eine spekulative (oder vorausschauende) Ausführung von Operationen erstellen, was eine große Komplexität der Hardwarelogik des Prozessors erfordert. Wenn Sie im Programm im Voraus Threads (Befehlssequenzen) definieren, die unabhängig voneinander ausgeführt werden können, vereinfacht dies die Aufgabe erheblich (diese Methode ist auf Hardwareebene leicht zu implementieren). Diese Idee, die Dean Tulsen gehört (1955 von ihm an der University of Washington entwickelt), wird als simultanes Multithreading bezeichnet. Es wurde später von Intel als Hyper-Threading ( hyper-Threading). Beispielsweise wird ein einzelner Prozessor, auf dem mehrere Threads ausgeführt werden, vom Windows-Betriebssystem als mehrere Prozessoren wahrgenommen. Die Verwendung dieser Technologie erfordert wiederum ein angemessenes Maß an Software. Der maximale Effekt der Verwendung der Multithreading-Technologie liegt bei etwa 30%.

1.4 Multicore

Die Multithreading-Technologie ist eine Software-Implementierung von Multicore. Eine weitere Leistungssteigerung erfordert wie immer Änderungen an der Prozessorhardware. Die Komplikation von Systemen und Architekturen ist nicht immer effektiv. Es gibt eine gegenteilige Meinung: "Alles Geniale ist einfach!". Um die Leistung eines Prozessors zu steigern, ist es in der Tat überhaupt nicht erforderlich, seine Taktfrequenz zu erhöhen, um die logischen und Hardwarekomponenten zu komplizieren, da es ausreicht, nur die vorhandene Technologie zu rationalisieren und zu verfeinern. Diese Methode ist sehr vorteilhaft - es besteht keine Notwendigkeit, das Problem der Erhöhung der Wärmeableitung des Prozessors und der Entwicklung neuer teurer Geräte zur Herstellung von Mikroschaltungen zu lösen. Dieser Ansatz wurde im Rahmen der Multicore-Technologie implementiert - der Implementierung mehrerer Rechenkerne auf einem Einkristall. Wenn wir den Originalprozessor verwenden und die Leistungssteigerungen vergleichen, wenn verschiedene Möglichkeiten zur Leistungsverbesserung implementiert werden, ist es offensichtlich, dass die Verwendung der Multicore-Technologie die beste Option ist.

Wenn wir die Architekturen eines symmetrischen Multiprozessors mit einem Multicore vergleichen, werden sie sich als nahezu identisch herausstellen. Der Kerncache kann mehrstufig sein (lokal und gemeinsam genutzt, und Daten aus dem RAM können direkt in den L2-Cache geladen werden). Basierend auf den betrachteten Vorteilen multi-Core-Architektur Prozessoren, Hersteller konzentrieren sich darauf. Diese Technologie Es erwies sich als recht billig zu implementieren und universell, was es ermöglichte, es auf einen breiten Markt zu bringen. Darüber hinaus hat diese Architektur ihre eigenen Anpassungen am Mooreschen Gesetz vorgenommen: "Die Anzahl der Rechenkerne in einem Prozessor wird sich alle 18 Monate verdoppeln."

Blick auf den modernen Markt computertechnologieSie können sehen, dass Geräte mit Vier- und Achtkernprozessoren dominieren. Darüber hinaus sagen Prozessorhersteller, dass bald Prozessoren mit Hunderten von Prozessorkernen auf den Markt kommen werden. Wie schon oft gesagt, zeigt sich das volle Potenzial einer Multi-Core-Architektur nur mit hochwertiger Software. Somit ist der Bereich der Herstellung von Computerhardware und -software sehr eng miteinander verbunden.

Saul 9. September 2015 um 13:38 Uhr

Implementierung einer Multi-Threaded-Game-Engine-Architektur

  • Intel Blog,
  • Spieleentwicklung,
  • Parallele Programmierung,
  • Webseitenentwicklung
  • Transfer

Mit dem Aufkommen von Multi-Core-Prozessoren wurde es notwendig, eine Spiel-Engine zu erstellen, die auf einer parallelen Architektur basiert. Die Verwendung aller Prozessoren im System - sowohl Grafik (GPU) als auch Zentral (CPU) - eröffnet viel mehr Möglichkeiten als eine Single-Threaded-Engine, die nur auf GPU basiert. Die Verwendung von mehr CPU-Kernen kann beispielsweise die Grafik verbessern, indem die Anzahl der im Spiel verwendeten physischen Objekte erhöht wird, und durch die Implementierung fortschrittlicher künstlicher Intelligenz (KI) ein realistischeres Charakterverhalten erzielen.
Berücksichtigen Sie die Implementierungsfunktionen der Multithread-Architektur der Game Engine.

1. Einleitung

1.1. Überblick

Die Multithread-Architektur der Game Engine ermöglicht es Ihnen, die Funktionen aller Prozessoren auf der Plattform maximal zu nutzen. Es wird die parallele Ausführung verschiedener Funktionsblöcke auf allen verfügbaren Prozessoren vorausgesetzt. Es stellt sich jedoch heraus, dass die Implementierung eines solchen Schemas nicht so einfach ist. Einzelne Elemente der Spiel-Engine interagieren häufig miteinander, was zu Fehlern führen kann, wenn sie gleichzeitig ausgeführt werden. Um solche Szenarien zu handhaben, bietet die Engine spezielle Datensynchronisationsmechanismen, die dies ausschließen mögliche Verstopfungen... Außerdem werden Methoden zum gleichzeitigen Synchronisieren von Daten implementiert, wodurch die Ausführungszeit minimiert wird.

Um die vorgestellten Materialien zu verstehen, müssen Sie die modernen Methoden zum Erstellen von Computerspielen, zum Unterstützen von Multithreading für Spiele-Engines oder zum Verbessern der Anwendungsleistung im Allgemeinen gut kennen.

2. Status der parallelen Ausführung

Der Parallelitätsstatus ist ein Schlüsselkonzept für Multithreading. Nur wenn die Spiel-Engine in separate Systeme unterteilt wird, die jeweils in ihrem eigenen Modus arbeiten und praktisch nicht mit dem Rest der Engine interagieren, kann die höchste Effizienz des parallelen Rechnens erzielt und die für die Synchronisierung erforderliche Zeit verkürzt werden. Es ist nicht möglich, einzelne Teile der Engine vollständig zu isolieren, indem alle gemeinsam genutzten Ressourcen ausgeschlossen werden. Für Vorgänge wie das Abrufen von Positions- oder Orientierungsdaten können einzelne Systeme jedoch lokale Kopien der Daten anstelle von gemeinsam genutzten Ressourcen verwenden. Auf diese Weise können Sie die Datenabhängigkeit in verschiedenen Teilen der Engine minimieren. Änderungen an gemeinsam genutzten Daten, die von einem einzelnen System vorgenommen wurden, werden dem Statusmanager mitgeteilt, der sie in die Warteschlange stellt. Dies wird als Messaging-Modus bezeichnet. In diesem Modus wird davon ausgegangen, dass die Motorsysteme nach Abschluss der Aufgaben Benachrichtigungen über Änderungen erhalten und ihre internen Daten entsprechend aktualisieren. Dieser Mechanismus kann die Synchronisationszeit und die Abhängigkeit von Systemen voneinander erheblich reduzieren.

2.1 Ausführungszustände

Damit der Ausführungsstatusmanager effizient arbeiten kann, wird empfohlen, Vorgänge mit einer bestimmten Uhr zu synchronisieren. Dadurch können alle Systeme gleichzeitig ausgeführt werden. Die Taktrate muss jedoch nicht mit der Bildrate übereinstimmen. Und die Dauer der Taktzyklen hängt möglicherweise nicht von der Frequenz ab. Es kann so ausgewählt werden, dass ein Zyklus der Zeit entspricht, die zum Senden eines Rahmens erforderlich ist (unabhängig von seiner Größe). Mit anderen Worten, die Häufigkeit oder Dauer von Zecken wird durch die spezifische Implementierung des Zustandsmanagers bestimmt. Abbildung 1 zeigt eine "freie" schrittmodus Arbeiten, bei denen nicht alle Systeme einen Vorgang im selben Taktzyklus ausführen müssen. Der Modus, in dem alle Systeme die Ausführung von Operationen in einem Taktzyklus abschließen, wird als "harter" Schrittmodus bezeichnet. Es ist schematisch in Abbildung 2 dargestellt.


Abbildung 1. Ausführungsstatus im kostenlosen Schritt-für-Schritt-Modus

2.1.1. Kostenloser Schritt-für-Schritt-Modus
In einem freien Schritt-für-Schritt-Modus arbeiten alle Systeme kontinuierlich für einen vorbestimmten Zeitraum, der erforderlich ist, um den nächsten Teil der Berechnungen abzuschließen. Der Name „frei“ sollte jedoch nicht wörtlich genommen werden: Systeme werden nicht zu einem beliebigen Zeitpunkt synchronisiert, sondern nur „frei“ bei der Auswahl der Anzahl der Taktzyklen, die für den Abschluss der nächsten Stufe erforderlich sind.
In diesem Modus reicht es normalerweise nicht aus, eine einfache Benachrichtigung über eine Statusänderung an den Statusmanager zu senden. Die aktualisierten Daten müssen ebenfalls gesendet werden. Dies liegt daran, dass das System, das die freigegebenen Daten geändert hat, möglicherweise ausgeführt wird, während ein anderes System, das auf diese Daten wartet, zur Aktualisierung bereit ist. In diesem Fall wird mehr Speicher benötigt, da mehr Kopien der Daten erstellt werden müssen. Daher kann das "freie" Regime nicht für alle Gelegenheiten als universelle Lösung angesehen werden.
2.1.2. Harter Schrittmodus
In diesem Modus wird die Ausführung von Aufgaben auf allen Systemen in einem Zyklus abgeschlossen. Dieser Mechanismus ist einfacher zu implementieren und erfordert nicht die Übertragung aktualisierter Daten zusammen mit der Benachrichtigung. In der Tat kann ein System bei Bedarf einfach neue Werte von einem anderen System anfordern (natürlich am Ende des Laufzyklus).
Im harten Modus können Sie einen pseudofreien Schrittbetriebsmodus implementieren, indem Sie Berechnungen auf verschiedene Schritte verteilen. Dies kann insbesondere für AI-Berechnungen erforderlich sein, bei denen im ersten Taktzyklus ein anfängliches "gemeinsames Ziel" berechnet wird, das in den folgenden Stufen schrittweise verfeinert wird.


Abbildung 2. Ausführungsstatus im harten Schritt-für-Schritt-Modus

2.2. Datensynchronisation

Änderungen an gemeinsam genutzten Daten durch mehrere Systeme können zu einem Änderungskonflikt führen. In diesem Fall muss das Nachrichtensystem einen Algorithmus zur Auswahl des richtigen Gesamtwerts bereitstellen. Es gibt zwei Hauptansätze, die auf den folgenden Kriterien basieren.
  • Zeit: Die Summe ist die letzte vorgenommene Änderung.
  • Priorität: Die Summe ist die Änderung, die vom System mit der höchsten Priorität vorgenommen wurde. Wenn die Priorität der Systeme gleich ist, können Sie auch den Zeitpunkt der Änderungen berücksichtigen.
Alle veralteten Daten (für eines der Kriterien) können einfach überschrieben oder aus der Benachrichtigungswarteschlange ausgeschlossen werden.
Da die Summe in Abhängigkeit von der Reihenfolge variieren kann, in der die Änderungen vorgenommen werden, kann es sehr schwierig sein, die relativen Werte der Gesamtdaten zu verwenden. In solchen Fällen sollten absolute Werte verwendet werden. Beim Aktualisieren lokaler Daten können Systeme dann einfach die alten Werte durch neue ersetzen. Optimale Lösung - Wählen Sie je nach Situation absolute oder relative Werte. Beispielsweise müssen allgemeine Daten wie Position und Ausrichtung absolut sein, da die Reihenfolge, in der Änderungen vorgenommen werden, wichtig ist. Relative Werte können beispielsweise für ein Partikelerzeugungssystem verwendet werden, da alle Informationen über Partikel nur darin gespeichert sind.

3. Motor

Bei der Entwicklung des Motors liegt das Hauptaugenmerk auf der Flexibilität, die zur weiteren Erweiterung seiner Funktionalität erforderlich ist. Dadurch wird es für die Verwendung unter bestimmten Einschränkungen (z. B. Speicher) optimiert.
Die Engine kann grob in zwei Teile unterteilt werden: Framework und Manager. Das Framework (siehe Abschnitt 3.1) enthält Teile des Spiels, die zur Laufzeit repliziert werden, dh in mehreren Fällen vorhanden sind. Es enthält auch die Elemente, die an der Ausführung der Hauptspielschleife beteiligt sind. Manager (siehe Abschnitt 3.2) sind Singleton-Objekte, die für die Ausführung der Spiellogik verantwortlich sind.
Unten sehen Sie ein Diagramm der Spiel-Engine.


Abbildung 3. Allgemeine Architektur des Motors

Bitte beachten Sie, dass funktionale Spielmodule oder Systeme nicht Teil der Engine sind. Der Motor verbindet sie nur als Verbindungselement. Diese modulare Organisation ermöglicht das Laden und Entladen von Systemen nach Bedarf.

Das Zusammenspiel von Motor und System erfolgt über Schnittstellen. Sie sind so implementiert, dass der Motor Zugriff auf Systemfunktionen und Systeme hat - auf die Motormanager.
Ein detailliertes Motordiagramm finden Sie in Anhang A, Motordiagramm.

Praktisch alle Systeme sind unabhängig voneinander (siehe Abschnitt 2, "Gleichzeitiger Ausführungsstatus"), dh sie können Aktionen parallel ausführen, ohne den Betrieb anderer Systeme zu beeinträchtigen. Jede Datenänderung bringt jedoch gewisse Schwierigkeiten mit sich, da die Systeme miteinander interagieren müssen. Der Informationsaustausch zwischen Systemen ist in folgenden Fällen erforderlich:

  • ein anderes System über eine Änderung allgemeiner Daten (z. B. die Position oder Ausrichtung von Objekten) zu informieren;
  • um Funktionen auszuführen, die für dieses System nicht verfügbar sind (zum Beispiel wendet sich das AI-System an das System, um die geometrischen oder physikalischen Eigenschaften eines Objekts zu berechnen, um einen Strahlkreuzungstest durchzuführen).
Im ersten Fall kann der im vorherigen Abschnitt beschriebene Zustandsmanager zur Steuerung des Informationsaustauschs verwendet werden. (Weitere Informationen zum Statusmanager finden Sie in Abschnitt 3.2.2, "Der Statusmanager".)
Im zweiten Fall muss ein spezieller Mechanismus implementiert werden, der die Dienste eines Systems für die Verwendung eines anderen bereitstellt. Eine vollständige Beschreibung dieses Mechanismus finden Sie in Abschnitt 3.2.3, Service Manager.

3.1. Rahmen

Das Framework wird verwendet, um alle Elemente des Motors zu kombinieren. Hier wird die Engine initialisiert, mit Ausnahme von Managern, die global instanziiert werden. Es speichert auch Informationen über die Szene. Um eine größere Flexibilität zu erreichen, wird die Szene als sogenannte generische Szene implementiert, die generische Objekte enthält. Sie sind Container, die verschiedene funktionale Teile der Szene kombinieren. Einzelheiten siehe Abschnitt 3.1.2.
Die Hauptschleife des Spiels ist ebenfalls im Framework implementiert. Es kann wie folgt schematisch dargestellt werden.


Abbildung 4. Hauptspielschleife

Die Engine läuft in einer Fensterumgebung, daher muss der erste Schritt in der Spieleschleife alle unvollendeten OS-Fenstermeldungen verarbeiten. Andernfalls reagiert die Engine nicht auf Betriebssystemnachrichten. Im zweiten Schritt weist der Scheduler Aufgaben über den Task-Manager zu. Dieser Vorgang wird in Abschnitt 3.1.1 beschrieben. Danach sendet der Zustandsmanager (siehe Abschnitt 3.2.2) Informationen über die an den Motorsystemen vorgenommenen Änderungen, deren Arbeit davon betroffen sein kann. Im letzten Schritt bestimmt das Framework abhängig vom Ausführungsstatus, ob die Engine beendet oder fortgesetzt werden soll, um beispielsweise zur nächsten Szene zu wechseln. Informationen zum Status der Engine werden vom Umgebungsmanager gespeichert. Einzelheiten finden Sie in Abschnitt 3.2.4.

3.1.1. Planer
Der Scheduler generiert einen Ausführungsreferenztakt mit einer bestimmten Rate. Wenn im Benchmark-Modus der nächste Vorgang unmittelbar nach Abschluss des vorherigen Vorgangs gestartet werden muss, ohne auf das Ende der Uhr zu warten, kann die Frequenz unbegrenzt sein.
Bei einem Taktsignal verwendet der Scheduler den Task-Manager, um die Systeme in den Ausführungsmodus zu versetzen. Im Freischrittmodus (Abschnitt 2.1.1) fragt der Scheduler die Systeme ab, um festzustellen, wie viele Taktzyklen sie benötigen, um die Aufgabe abzuschließen. Basierend auf den Ergebnissen der Umfrage bestimmt der Planer, welche Systeme zur Ausführung bereit sind und welche ihre Arbeit zu einem bestimmten Zeitschritt abschließen. Der Scheduler kann die Anzahl der Ticks ändern, wenn die Ausführung eines Systems länger dauert. Im Hard-Stepping-Modus (Abschnitt 2.1.2) starten und enden alle Systeme im selben Taktzyklus, sodass der Scheduler darauf wartet, dass alle Systeme die Ausführung beenden.
3.1.2. Vielseitige Szene und Objekte
Generische Szenen und Objekte sind Container für Funktionen, die in anderen Systemen implementiert sind. Sie dienen ausschließlich der Interaktion mit dem Motor und erfüllen keine andere Funktion. Sie können jedoch erweitert werden, um Funktionen zu nutzen, die anderen Systemen zur Verfügung stehen. Dies ermöglicht eine lose Kupplung. In der Tat können eine universelle Szene und Objekte die Eigenschaften anderer Systeme nutzen, ohne an sie gebunden zu sein. Es ist diese Eigenschaft, die die Abhängigkeit von Systemen voneinander beseitigt und es ihnen ermöglicht, gleichzeitig zu arbeiten.
Das folgende Diagramm zeigt eine Erweiterung einer universellen Szene und eines universellen Objekts.


Abbildung 5. Erweitern der universellen Szene und des universellen Objekts

Sehen wir uns im folgenden Beispiel an, wie Erweiterungen funktionieren. Angenommen, die Erweiterung der universellen universellen Szene wird ausgeführt, und die Szene wird erweitert, um die Verwendung grafischer, physikalischer und anderer Eigenschaften zu verwenden. In diesem Fall ist der "grafische" Teil der Erweiterung für die Initialisierung der Anzeige verantwortlich, und sein "physischer" Teil ist für die Implementierung physikalischer Gesetze für starre Körper, beispielsweise die Schwerkraft, verantwortlich. Szenen enthalten Objekte, sodass eine generische Szene auch mehrere generische Objekte enthält. Generische Objekte können auch erweitert und um grafische, physikalische und andere Eigenschaften erweitert werden. Zum Beispiel wird das Zeichnen eines Objekts auf dem Bildschirm durch grafische Erweiterungsfunktionen und die Berechnung der Wechselwirkung von Festkörpern - durch physikalische - implementiert.

Ein detailliertes Diagramm des Zusammenspiels von Motor und Systemen finden Sie in Anhang B, "Schema des Zusammenspiels von Motor und Systemen".
Es ist zu beachten, dass die generische Szene und das generische Objekt für die Registrierung aller ihrer "Erweiterungen" beim Statusmanager verantwortlich sind, damit alle Erweiterungen über Änderungen informiert werden können, die von anderen Erweiterungen (dh anderen Systemen) vorgenommen wurden. Ein Beispiel ist eine Grafikerweiterung, die registriert ist, um Benachrichtigungen über Positions- und Orientierungsänderungen zu erhalten, die durch die physische Erweiterung vorgenommen wurden.
Einzelheiten zu Systemkomponenten finden Sie in Abschnitt 5.2, Systemkomponenten.

3.2. Manager

Manager steuern die Arbeit des Motors. Es handelt sich um Singleton-Objekte, dh jeder Managertyp ist nur in einer Instanz verfügbar. Dies ist erforderlich, da das Duplizieren von Managerressourcen zwangsläufig zu Redundanz führt und sich negativ auf die Leistung auswirkt. Darüber hinaus sind Manager für die Implementierung gemeinsamer Funktionen für alle Systeme verantwortlich.
3.2.1. Taskmanager
Der Task-Manager ist für die Verwaltung der Systemaufgaben im Thread-Pool verantwortlich. Der Thread-Pool erstellt einen Thread für jeden Prozessor, um eine optimale n-fache Skalierung zu gewährleisten und zu verhindern, dass unnötige Threads zugewiesen werden, wodurch der unnötige Aufwand für das Umschalten des Betriebssystems entfällt.

Der Scheduler gibt dem Task-Manager eine Liste der zu erledigenden Aufgaben sowie Informationen darüber, welche Aufgaben auf den Abschluss warten sollen. Er erhält diese Daten von verschiedene Systeme... Jedes System hat nur eine Aufgabe zu erledigen. Diese Methode wird als funktionale Zerlegung bezeichnet. Für die Datenverarbeitung kann jede solche Aufgabe jedoch in eine beliebige Anzahl von Unteraufgaben unterteilt werden (Datenzerlegung).
Im Folgenden finden Sie ein Beispiel für die Verteilung von Aufgaben auf Threads für ein Quad-Core-System.


Abbildung 6. Beispiel eines Thread-Pools, der von einem Task-Manager verwendet wird

Zusätzlich zur Verarbeitung von Scheduler-Anforderungen für den Zugriff auf Hauptaufgaben kann der Task-Manager im Initialisierungsmodus arbeiten. Es fragt nacheinander die Systeme von jedem Thread ab, damit sie die für den Betrieb erforderlichen lokalen Datenspeicher initialisieren können.
Tipps zum Implementieren eines Task-Managers finden Sie in Anhang D, Tipps zum Implementieren von Aufgaben.

3.2.2. Staatsmanager
Der Statusmanager ist Teil der Messaging-Engine. Es überwacht Änderungen und sendet Benachrichtigungen über diese an alle Systeme, die von diesen Änderungen betroffen sein können. Um keine unnötigen Benachrichtigungen zu versenden, speichert der Statusmanager Informationen darüber, welche Systeme in einem bestimmten Fall benachrichtigt werden sollen. Dieser Mechanismus wird mithilfe des Observer-Musters implementiert (siehe Anhang C, Observer (Design Pattern)). Zusamenfassend, gegebene Vorlage beinhaltet die Verwendung eines "Beobachters", der alle Änderungen im Subjekt überwacht, während die Rolle eines Vermittlers zwischen ihnen vom Änderungscontroller gespielt wird.

Der Mechanismus funktioniert wie folgt. 1. Der Beobachter teilt dem Änderungscontroller (oder Statusmanager) mit, welche Entitäten er für Änderungen verfolgen möchte. 2. Der Betreff benachrichtigt den Controller über alle seine Änderungen. 3. Auf ein Signal vom Framework hin benachrichtigt der Controller den Beobachter über Änderungen des Subjekts. 4. Der Beobachter sendet eine Anfrage an das Subjekt, um aktualisierte Daten zu erhalten.

Im Freischrittmodus (siehe Abschnitt 2.1.1) ist die Implementierung dieses Mechanismus etwas kompliziert. Zunächst müssen die aktualisierten Daten zusammen mit der Änderungsbenachrichtigung gesendet werden. In diesem Modus ist Send on Demand nicht anwendbar. Wenn das für die Änderungen verantwortliche System zum Zeitpunkt des Empfangs der Anforderung noch nicht vollständig ausgeführt ist, kann es keine aktualisierten Daten bereitstellen. Zweitens, wenn ein System am Ende eines Taktzyklus noch nicht bereit ist, Änderungen zu empfangen, muss der Statusmanager die geänderten Daten beibehalten, bis alle für den Empfang registrierten Systeme bereit sind.

Das Framework bietet hierfür zwei Statusmanager: zum Verarbeiten von Änderungen auf Szenenebene und auf Objektebene. In der Regel sind Nachrichten zu Szenen und Objekten unabhängig voneinander, sodass bei Verwendung von zwei separaten Managern keine unnötigen Daten verarbeitet werden müssen. Wenn die Szene jedoch den Status eines Objekts berücksichtigen muss, können Sie es registrieren, um Benachrichtigungen über seine Änderungen zu erhalten.

Um unnötige Synchronisierungen zu vermeiden, generiert der Statusmanager für jeden vom Task-Manager erstellten Thread separat eine Änderungsbenachrichtigungswarteschlange. Daher ist beim Zugriff auf die Warteschlange keine Synchronisierung erforderlich. Abschnitt 2.2 beschreibt eine Methode, mit der Warteschlangen nach der Ausführung zusammengeführt werden können.


Abbildung 7. Benachrichtigung über interne Änderungen an einem generischen Objekt

Änderungsbenachrichtigungen müssen nicht nacheinander gesendet werden. Es gibt eine Möglichkeit, sie parallel zu senden. Während der Ausführung der Aufgabe arbeitet das System mit allen seinen Objekten. Wenn beispielsweise physische Objekte miteinander interagieren, steuert das physische System ihre Bewegung, die Berechnung von Kollisionen, neuen Wirkkräften usw. Beim Empfang von Benachrichtigungen interagiert das Systemobjekt nicht mit anderen Objekten in seinem System. Es interagiert mit den zugehörigen generischen Objekterweiterungen. Dies bedeutet, dass generische Objekte jetzt unabhängig voneinander sind und gleichzeitig aktualisiert werden können. Dieser Ansatz schließt Kantenfälle nicht aus, die während des Synchronisationsprozesses berücksichtigt werden sollten. Es ermöglicht jedoch die Verwendung des parallelen Ausführungsmodus, wenn es den Anschein hat, dass Sie nur sequentiell handeln können.

3.2.3. Service Manager
Der Service Manager bietet Systemen Zugriff auf Funktionen auf anderen Systemen, die ihnen sonst nicht zur Verfügung stehen würden. Es ist wichtig zu verstehen, dass auf Funktionen nicht direkt, sondern über Schnittstellen zugegriffen wird. Die Swerden auch im Service Manager gespeichert.
Um die Abhängigkeit von Systemen voneinander auszuschließen, hat jeder von ihnen nur kleines Set Dienstleistungen. Darüber hinaus wird die Fähigkeit zur Verwendung eines bestimmten Dienstes nicht vom System selbst, sondern vom Dienstmanager bestimmt.


Abbildung 8. Beispiel für einen Service Manager

Der Service Manager hat noch eine andere Funktion. Es bietet Systemen Zugriff auf Eigenschaften anderer Systeme. Eigenschaften sind systemspezifische Werte, die im Nachrichtensystem nicht kommuniziert werden. Dies kann eine Erweiterung der Bildschirmauflösung in einem Grafiksystem oder der Schwerkraft in einem physikalischen System sein. Der Service Manager stellt diese Daten den Systemen zur Verfügung, steuert sie jedoch nicht direkt. Eigenschaftsänderungen werden in eine spezielle Warteschlange gestellt und erst nach sequentieller Ausführung veröffentlicht. Bitte beachten Sie, dass der Zugriff auf Eigenschaften eines anderen Systems selten erforderlich ist und nicht missbraucht werden sollte. Beispielsweise benötigen Sie es möglicherweise, um den Drahtgittermodus im Grafiksystem über das Konsolenfenster ein- und auszuschalten oder um die vom Player über die Benutzeroberfläche angeforderte Bildschirmauflösung zu ändern. Diese Funktion wird hauptsächlich zum Einstellen von Parametern verwendet, die sich nicht von Bild zu Bild ändern.

3.2.4. Umweltmanager
  • Der Umgebungsmanager stellt die Engine-Laufzeitumgebung bereit. Seine Funktionen können bedingt in die folgenden Gruppen unterteilt werden.
  • Variablen: Die Namen und Werte der allgemeinen Variablen, die von allen Teilen der Engine verwendet werden. In der Regel werden die Werte von Variablen beim Laden der Szene oder bei bestimmten Benutzereinstellungen festgelegt. Die Engine und verschiedene Systeme können durch Senden einer Anfrage darauf zugreifen.
  • Ausführung: Ausführungsdaten, z. B. die Fertigstellung einer Szene oder eines Programms. Diese Parameter können sowohl vom System selbst als auch vom Motor eingestellt und angefordert werden.
3.2.5. Plattformmanager
Der Plattformmanager implementiert die Abstraktion für Anrufe betriebssystemund bietet zusätzliche Funktionen, die über die einfache Abstraktion hinausgehen. Der Vorteil dieses Ansatzes besteht darin, dass mehrere typische Funktionen in einem einzigen Aufruf zusammengefasst sind. Das heißt, sie müssen nicht für jeden Anrufer separat implementiert werden, da sie mit Details zu Betriebssystemaufrufen überladen sind.
Rufen Sie als Beispiel den Plattformmanager zum Herunterladen auf dynamische Bibliothek Systeme. Es lädt nicht nur das System, sondern ruft auch die Funktionseintrittspunkte ab und ruft die Bibauf. Der Manager speichert auch den Bibliotheksdeskriptor und entlädt ihn nach Beendigung der Engine.

Der Plattformmanager ist auch dafür verantwortlich, Informationen über den Prozessor bereitzustellen, z. B. unterstützte SIMD-Anweisungen, und einen bestimmten Betriebsmodus für Prozesse zu initialisieren. Die Systeme können keine anderen Funktionen zum Generieren von Abfragen verwenden.

4. Schnittstellen

Schnittstellen sind das Kommunikationsmittel zwischen Framework, Managern und Systemen. Das Framework und die Manager sind Teil der Engine, sodass sie direkt miteinander interagieren können. Systeme gehören nicht zum Motor. Darüber hinaus erfüllen sie alle unterschiedliche Funktionen, was dazu führt, dass eine einzige Interaktionsmethode mit ihnen erstellt werden muss. Da Systeme nicht direkt mit Managern kommunizieren können, müssen sie eine andere Zugriffsmethode bereitstellen. Es sollten jedoch nicht alle Funktionen von Managern für Systeme offen sein. Einige von ihnen stehen nur dem Framework zur Verfügung.

Schnittstellen definieren die für die Verwendung erforderlichen Funktionen standardmethode Zugriff. Dadurch muss das Framework die Implementierungsdetails bestimmter Systeme nicht mehr kennen, da es nur über bestimmte Aufrufe mit ihnen interagieren kann.

4.1. Betreff- und Beobachterschnittstellen

Der Hauptzweck der Subjekt- und Beobachterschnittstellen besteht darin, zu registrieren, über welche Beobachter Benachrichtigungen gesendet werden sollen, und diese Benachrichtigungen auch zu senden. Das Registrieren und Unterbrechen der Verbindung mit dem Beobachter sind Standardfunktionen für alle Subjekte, die in der Implementierung ihrer Schnittstelle enthalten sind.

4.2. Manager-Schnittstellen

Manager sind zwar Singleton-Objekte, aber nur für das Framework direkt zugänglich. Andere Systeme können nur über Schnittstellen auf Manager zugreifen, die nur einen Bruchteil ihrer Gesamtfunktionalität darstellen. Nach der Initialisierung wird die Schnittstelle an das System übergeben, das sie für die Arbeit mit bestimmten Managerfunktionen verwendet.
Es gibt keine einzige Schnittstelle für alle Manager. Jeder von ihnen hat eine eigene Schnittstelle.

4.3. Systemschnittstellen

Damit ein Framework auf Systemkomponenten zugreifen kann, benötigt es Schnittstellen. Ohne sie die Unterstützung aller neues System Der Motor müsste separat implementiert werden.
Jedes System enthält vier Komponenten, daher sollten vier Schnittstellen vorhanden sein. Nämlich: System, Szene, Objekt und Aufgabe. Eine detaillierte Beschreibung finden Sie in Abschnitt 5, Systeme. Schnittstellen sind die Mittel für den Zugriff auf Komponenten. Über die Systemschnittstellen können Sie Szenen erstellen und löschen. Mit Szenenschnittstellen können Sie wiederum Objekte erstellen und zerstören sowie Informationen zur Hauptaufgabe des Systems anfordern. Die Aufgabenschnittstelle wird hauptsächlich vom Aufgabenmanager verwendet, wenn Aufgaben für den Thread-Pool festgelegt werden.
Da die Szene und das Objekt als Teil des Systems miteinander und mit der universellen Szene und dem Objekt, an das sie gebunden sind, interagieren müssen, werden ihre Schnittstellen auch auf der Grundlage der Schnittstellen des Subjekts und des Betrachters erstellt.

4.4. Schnittstellen ändern

Diese Schnittstellen werden zum Übertragen von Daten zwischen Systemen verwendet. Alle Systeme, die Änderungen eines bestimmten Typs vornehmen, müssen eine solche Schnittstelle implementieren. Geometrie ist ein Beispiel. Die Geometrieschnittstelle enthält Methoden zum Bestimmen der Position, Ausrichtung und Skalierung eines Elements. Jedes System, das Änderungen an der Geometrie vornimmt, muss eine solche Schnittstelle implementieren, damit keine Informationen über andere Systeme für den Zugriff auf die geänderten Daten erforderlich sind.

5. Systeme

Systeme sind der Teil der Engine, der für die Implementierung der Spielfunktionalität verantwortlich ist. Sie führen alle grundlegenden Aufgaben aus, ohne die der Motor keinen Sinn ergeben würde. Die Interaktion zwischen Motor und System erfolgt über Schnittstellen (siehe Abschnitt 4.3, "Systemschnittstellen"). Dies ist notwendig, um den Motor nicht mit Informationen über verschiedene Systemtypen zu überlasten. Schnittstellen erleichtern das Hinzufügen eines neuen Systems erheblich, da die Engine nicht alle Implementierungsdetails berücksichtigen muss.

5.1. Typen

Engine-Systeme können grob in mehrere vordefinierte Kategorien unterteilt werden, die den Standardspielkomponenten entsprechen. Zum Beispiel: Geometrie, Grafik, Physik (feste Kollision), Sound, Eingabeverarbeitung, KI und Animation.
Systeme mit nicht standardmäßigen Funktionen fallen in eine separate Kategorie. Es ist wichtig zu verstehen, dass jedes System, das Daten für eine bestimmte Kategorie ändert, die Schnittstelle dieser Kategorie kennen muss, da die Engine solche Informationen nicht bereitstellt.

5.2. Systemkomponenten

Für jedes System müssen mehrere Komponenten implementiert werden. Einige davon sind: System, Szene, Objekt und Aufgabe. Alle diese Komponenten werden verwendet, um mit verschiedenen Teilen des Motors zu interagieren.
Das folgende Diagramm zeigt die Wechselwirkungen zwischen den verschiedenen Komponenten.


Abbildung 9. Systemkomponenten

Ein detailliertes Diagramm der Verbindungen zwischen den Motorsystemen finden Sie in Anhang B, „Schema der Wechselwirkung zwischen Motor und Systemen“.

5.2.1. System
Die "System" -Komponente oder einfach das System ist für die Initialisierung der Systemressourcen verantwortlich, die sich während des Motorbetriebs praktisch nicht ändern. Das Grafiksystem analysiert beispielsweise Ressourcenadressen, um festzustellen, wo sie sich befinden, und um das Laden bei Verwendung der Ressource zu beschleunigen. Außerdem wird die Bildschirmauflösung festgelegt.
Das System ist der Haupteinstiegspunkt für das Framework. Es enthält Informationen über sich selbst (z. B. den Systemtyp) sowie Methoden zum Erstellen und Löschen von Szenen.
5.2.2. Szene
Die Szenenkomponente oder Systemszene ist für die Verwaltung der der aktuellen Szene zugeordneten Ressourcen verantwortlich. Die generische Szene verwendet Systemszenen, um die Funktionalität durch Nutzung ihrer Funktionalität zu erweitern. Ein Beispiel ist eine physische Szene, die zum Erstellen einer neuen Spielwelt verwendet wird und bei der Initialisierung der Szene die darin enthaltenen Schwerkraftkräfte bestimmt.
Szenen bieten Methoden zum Erstellen und Zerstören von Objekten sowie eine "Aufgaben" -Komponente zum Verarbeiten der Szene und eine Methode zum Zugreifen darauf.
5.2.3. Ein Objekt
Die Objektkomponente oder das Systemobjekt gehört zur Szene und ist normalerweise mit dem verknüpft, was der Benutzer auf dem Bildschirm sieht. Ein generisches Objekt verwendet ein Systemobjekt, um die Funktionalität zu erweitern, indem seine Eigenschaften als eigene verfügbar gemacht werden.
Ein Beispiel wäre die geometrische, grafische und physische Erweiterung eines generischen Objekts, um einen Holzbalken auf einem Bildschirm anzuzeigen. Zu den geometrischen Eigenschaften gehören die Position, Ausrichtung und Skalierung des Objekts. Das Grafiksystem verwendet ein spezielles Raster, um es anzuzeigen. Und das physikalische System wird es mit den Eigenschaften eines starren Körpers ausstatten, um Wechselwirkungen mit anderen Körpern und die wirkenden Schwerkraftkräfte zu berechnen.

In bestimmten Fällen muss ein Systemobjekt Änderungen an einem generischen Objekt oder einer seiner Erweiterungen berücksichtigen. Zu diesem Zweck können Sie eine spezielle Beziehung erstellen, die die vorgenommenen Änderungen verfolgt.

5.2.4. Aufgabe
Die Aufgabenkomponente oder Systemaufgabe wird zum Verarbeiten einer Szene verwendet. Die Aufgabe erhält vom Aufgabenmanager einen Befehl zum Aktualisieren der Szene. Dies ist das Signal zum Laufen systemfunktionen auf Szenenobjekten.
Die Ausführung einer Aufgabe kann in Unteraufgaben unterteilt werden, die auch über den Aufgabenmanager an eine noch größere Anzahl von Threads verteilt werden. Dies ist eine bequeme Möglichkeit, die Engine auf mehrere Prozessoren zu skalieren. Diese Methode wird als Datenzerlegung bezeichnet.
Informationen über Objektänderungen beim Aktualisieren der Szenenaufgaben werden an den Statusmanager übergeben. Einzelheiten zum Statusmanager finden Sie in Abschnitt 3.2.2.

6. Alle Komponenten kombinieren

Alle oben beschriebenen Elemente sind miteinander verwandt und Teil eines Ganzen. Die Arbeit des Motors kann grob in mehrere Stufen unterteilt werden, die in den folgenden Abschnitten beschrieben werden.

6.1. Initialisierungsphase

Die Engine startet mit der Initialisierung der Manager und des Frameworks.
  • Das Framework ruft den Szenenlader auf.
  • Nachdem festgelegt wurde, welche Systeme die Szene verwenden soll, ruft der Loader den Plattformmanager auf, um die entsprechenden Module zu laden.
  • Der Plattformmanager lädt die entsprechenden Module, übergibt sie an den Schnittstellenmanager und ruft sie dann auf, um ein neues System zu erstellen.
  • Das Modul gibt dem Loader einen Zeiger auf eine Instanz des Systems zurück, die die Systemschnittstelle implementiert.
  • Der Service Manager registriert alle Services, die das Systemmodul bereitstellt.


Abbildung 10. Initialisierung von Managern und Motorsystemen

6.2. Ladephase der Szene

Die Steuerung wird an den Loader zurückgegeben, der die Szene lädt.
  • Der Loader erstellt eine generische Szene. Um Systemszenen zu instanziieren, werden die Systemschnittstellen aufgerufen, wodurch die Funktionalität der generischen Szene erweitert wird.
  • Die universelle Szene definiert, welche Daten jede Systemszene ändern kann, und benachrichtigt darüber, welche Änderungen sie erhalten soll.
  • Durch das Abgleichen von Szenen, die bestimmte Änderungen vornehmen und darüber benachrichtigt werden möchten, gibt die generische Szene diese Informationen an den Statusmanager weiter.
  • Für jedes Objekt in der Szene erstellt der Loader ein generisches Objekt und bestimmt dann, welche Systeme das generische Objekt erweitern. Die Entsprechung zwischen Systemobjekten wird nach demselben Schema bestimmt, das für Szenen verwendet wird. Es wird auch an den Staatsmanager weitergegeben.
  • Der Loader verwendet die resultierenden Szenenschnittstellen, um Systemobjekte zu instanziieren und generische Objekte zu erweitern.
  • Der Scheduler fragt die Szenenschnittstellen nach ihren Hauptaufgaben ab, damit diese Informationen während der Ausführung an den Task-Manager übergeben werden können.


Abbildung 11. Initialisierung der universellen Szene und des Universums

6.3. Spielzyklusphase

  • Der Plattformmanager wird verwendet, um Fenstermeldungen und andere Elemente zu verarbeiten, die für das Funktionieren der aktuellen Plattform erforderlich sind.
  • Dann geht die Steuerung an den Scheduler über, der darauf wartet, dass das Ende der Uhr weiterarbeitet.
  • Am Ende der Uhr prüft der Scheduler in einem kostenlosen Schritt-für-Schritt-Modus, welche Aufgaben erledigt wurden. Alle abgeschlossenen Aufgaben (dh bereit zur Ausführung) werden an den Task-Manager übertragen.
  • Der Scheduler bestimmt, welche Aufgaben im aktuellen Tick erledigt werden, und wartet, bis sie erledigt sind.
  • Im Hard-Step-Modus werden diese Vorgänge bei jeder Messung wiederholt. Der Scheduler sendet alle Aufgaben an den Manager und wartet, bis sie abgeschlossen sind.
6.3.1. Aufgabe erledigen
Die Steuerung wird an den Task-Manager übertragen.
  • Es bildet eine Warteschlange aller empfangenen Aufgaben und startet dann, sobald freie Threads angezeigt werden, deren Ausführung. (Der Prozess der Ausführung von Aufgaben ist je nach System unterschiedlich. Systeme können nur mit einer Aufgabe arbeiten oder mehrere Aufgaben gleichzeitig aus der Warteschlange verarbeiten, wodurch eine parallele Ausführung realisiert wird.)
  • Während der Ausführung können Aufgaben mit der gesamten Szene oder nur mit bestimmten Objekten arbeiten und deren interne Daten ändern.
  • Systeme sollten über Änderungen allgemeiner Daten (z. B. Position oder Ausrichtung) informiert werden. Daher informiert die Systemszene oder das Objekt den Beobachter bei der Ausführung einer Aufgabe über Änderungen. In diesem Fall fungiert der Beobachter tatsächlich als Änderungskontrolleur, der Teil des Zustandsmanagers ist.
  • Der Änderungscontroller generiert eine Warteschlange mit Änderungsbenachrichtigungen zur weiteren Verarbeitung. Änderungen, die den jeweiligen Beobachter nicht betreffen, werden ignoriert.
  • Um bestimmte Dienste zu verwenden, kontaktiert eine Aufgabe den Dienstmanager. Mit dem Service Manager können Sie auch Eigenschaften anderer Systeme ändern, die nicht für die Übertragung in der Messaging-Engine verfügbar sind (z. B. ändert das Dateneingabesystem die Bildschirmerweiterung - eine Eigenschaft des Grafiksystems).
  • Aufgaben können sich auch an den Umgebungsmanager wenden, um Umgebungsvariablen abzurufen und den Ausführungsstatus zu ändern (Ausführung anhalten, mit der nächsten Szene fortfahren usw.).


Abbildung 12. Task-Manager und Aufgaben

6.3.2. Daten aktualisieren
Nachdem alle Aufgaben für das aktuelle Häkchen erledigt wurden, ruft die Hauptspielschleife den Statusmanager auf, um die Datenaktualisierungsphase zu starten.
  • Der Statusmanager ruft nacheinander jeden seiner Änderungscontroller auf, um die gesammelten Benachrichtigungen zu senden. Der Controller prüft, an welche Beobachter Änderungsbenachrichtigungen für jedes Thema gesendet werden sollen.
  • Es ruft dann den gewünschten Beobachter an und informiert ihn über die Änderung (die Benachrichtigung enthält auch einen Zeiger auf die Betreff-Oberfläche). Im freien Schrittmodus empfängt der Beobachter geänderte Daten vom Änderungsregler, im harten Schrittmodus muss er sie jedoch vom Subjekt selbst anfordern.
  • In der Regel sind Beobachter, die Benachrichtigungen über Systemobjektänderungen erhalten möchten, andere Systemobjekte, die demselben generischen Objekt zugeordnet sind. Auf diese Weise können Sie den Änderungsprozess in mehrere Aufgaben aufteilen, die parallel ausgeführt werden können. Um den Synchronisierungsprozess zu vereinfachen, können Sie alle zugehörigen generischen Objekterweiterungen in einer einzigen Aufgabe kombinieren.
6.3.3. Überprüfen Sie die Ausführung und beenden Sie sie
In der letzten Phase der Spielschleife wird der Status der Laufzeit überprüft. Es gibt mehrere solcher Zustände: Laufen, Angehalten, Nächste Szene usw. Wenn der Zustand "Laufen" ausgewählt ist, beginnt die nächste Iteration der Schleife. Der Exit-Status bedeutet das Ende der Schleife, die Freigabe von Ressourcen und das Beenden der Anwendung. Andere Zustände können implementiert werden, zum Beispiel "Pause", "nächste Szene" usw.

7. Fazit

Die Hauptidee dieses Artikels wird in Abschnitt 2, "Paralleler Ausführungsstatus" beschrieben. Dank der funktionalen Zerlegung und Datenzerlegung ist es möglich, nicht nur das Multithreading der Engine zu implementieren, sondern auch ihre Skalierbarkeit auf noch mehr Kerne in der Zukunft. Verwenden Sie neben Messaging auch Statusmanager, um den Synchronisierungsaufwand zu vermeiden und gleichzeitig Ihre Daten auf dem neuesten Stand zu halten.

Das Observer-Muster ist eine Funktion der Messaging-Engine. Es ist wichtig, gut zu verstehen, wie es funktioniert, um den besten Weg zu finden, es für den Motor zu implementieren. Tatsächlich handelt es sich um einen Mechanismus für die Interaktion zwischen verschiedenen Systemen, der die Synchronisation gemeinsamer Daten gewährleistet.

Das Aufgabenmanagement spielt eine wichtige Rolle beim Lastausgleich. Anhang D enthält Tipps zum Erstellen eines effektiven Task-Managers für eine Spiel-Engine.

Wie Sie sehen können, ist Game Engine Multithreading dank einer genau definierten Struktur und eines Messaging-Mechanismus möglich. Dies kann die Leistung aktueller und zukünftiger Prozessoren erheblich verbessern.

Anhang A. Motorschema

Die Verarbeitung wird von der Hauptspielschleife aus gestartet (siehe Abb. 4, „Hauptspielschleife“).


Anhang B. Schema der Wechselwirkung zwischen Motor und Systemen


Anhang C. Beobachter (Entwurfsmuster)

Das Observer-Muster wird im Buch Object Oriented Design Techniques ausführlich beschrieben. Entwurfsmuster ", E. Gamma, R. Helm, R. Johnson, J. Vlissides (" Entwurfsmuster: Elemente wiederverwendbarer objektorientierter Software ", Gamma E., Helm R., Johnson R., Vlissides J.). Es wurde erstmals 1995 von Addison-Wesley in englischer Sprache veröffentlicht.

Die Hauptidee dieses Modells lautet wie folgt: Wenn einige Elemente über Änderungen an anderen Elementen benachrichtigt werden müssen, müssen sie nicht die Liste aller möglichen Änderungen durchsehen und versuchen, die erforderlichen Daten darin zu finden. Das Modell setzt die Existenz eines Akteurs und eines Beobachters voraus, die zum Senden von Benachrichtigungen über Änderungen verwendet werden. Der Beobachter überwacht alle Änderungen am Thema. Der Change Controller fungiert als Vermittler zwischen diesen beiden Komponenten. Das folgende Diagramm veranschaulicht diese Beziehung.


Abbildung 13. Beobachtervorlage

Der Prozess der Verwendung dieses Modells wird unten beschrieben.

  1. Der Änderungscontroller registriert den Beobachter und das Subjekt, für das er benachrichtigt werden möchte.
  2. Der Change Controller ist eigentlich ein Beobachter. Anstelle des Beobachters registriert er sich beim Thema. Der Änderungskontrolleur führt auch eine eigene Liste von Beobachtern und bei ihnen registrierten Personen.
  3. Ein Proband fügt seiner Liste der Beobachter, die über seine Änderungen informiert werden möchten, einen Beobachter (dh einen Änderungscontroller) hinzu. Manchmal wird zusätzlich die Art der Änderung angegeben, die bestimmt, an welchen Änderungen der Beobachter interessiert ist. Auf diese Weise können Sie den Prozess des Versendens von Änderungsbenachrichtigungen optimieren.
  4. Beim Ändern von Daten oder Status benachrichtigt das Subjekt den Beobachter über einen Rückrufmechanismus und kommuniziert Informationen über die geänderten Typen.
  5. Der Änderungscontroller bildet eine Warteschlange mit Benachrichtigungen über Änderungen und wartet auf ein Signal, um diese auf Objekte und Systeme zu verteilen.
  6. Während der Verteilung spricht der Änderungscontroller echte Beobachter an.
  7. Beobachter fordern vom Betreff Informationen über geänderte Daten oder Zustände an (oder erhalten diese zusammen mit Benachrichtigungen).
  8. Bevor ein Beobachter gelöscht werden kann oder wenn er nicht mehr über einen Betreff benachrichtigt werden muss, wird das Abonnement für diesen Betreff im Änderungscontroller entfernt.
Da sind viele verschiedene Wege Aufgabenverteilung implementieren. Es ist jedoch am besten, die Anzahl der Arbeitsthreads gleich der Anzahl der verfügbaren logischen Plattformprozessoren zu halten. Versuchen Sie, Aufgaben nicht an einen bestimmten Thread zu binden. Die Ausführungszeit von Aufgaben verschiedener Systeme fällt nicht immer zusammen. Dies kann zu einer ungleichmäßigen Verteilung der Arbeitslast zwischen den Arbeitsthreads führen und die Effizienz beeinträchtigen. Um diesen Prozess zu vereinfachen, verwenden Sie Aufgabenverwaltungsbibliotheken wie

Betrachten Sie die Theorie des Multithreading praktisches Beispiel - Pentium 4. Bereits in der Entwicklungsphase dieses Prozessors arbeiteten die Intel-Ingenieure daran, die Leistung zu steigern, ohne Änderungen an der Programmoberfläche vorzunehmen. Es wurden fünf einfachste Wege in Betracht gezogen:

Erhöhen der Taktfrequenz;

Platzierung von zwei Prozessoren auf einer Mikroschaltung;

Einführung neuer Funktionsblöcke;

Verlängerung des Förderers;

Multithreading verwenden.

Der naheliegendste Weg, um die Leistung zu verbessern, besteht darin, die Taktrate zu erhöhen, ohne andere Parameter zu ändern. In der Regel hat jedes nachfolgende Prozessormodell eine etwas höhere Taktrate als das vorherige. Bei einer geradlinigen Erhöhung der Taktrate stehen Entwickler leider vor zwei Problemen: einem Anstieg des Stromverbrauchs (der für tragbare Computer und andere mit Batterien betriebene Computergeräte wichtig ist) und einer Überhitzung (die effizientere Kühlkörper erfordert).

Der zweite Weg - das Platzieren von zwei Prozessoren auf einer Mikroschaltung - ist relativ einfach, beinhaltet jedoch die Verdoppelung der von der Mikroschaltung eingenommenen Fläche. Wenn jeder Prozessor mit einem eigenen Cache-Speicher ausgestattet ist, halbiert sich die Anzahl der Chips auf einem Plattenteller, was jedoch auch eine Verdoppelung der Produktionskosten bedeutet. Durch die Bereitstellung eines gemeinsam genutzten Caches für beide Prozessoren kann eine erhebliche Erhöhung des Platzbedarfs vermieden werden. In diesem Fall tritt jedoch ein anderes Problem auf: Die Menge des Cache-Speichers pro Prozessor wird halbiert, was sich zwangsläufig auf die Leistung auswirkt. Während professionelle Serveranwendungen die Ressourcen mehrerer Prozessoren voll ausnutzen können, ist die interne Parallelität in normalen Desktop-Programmen viel weniger entwickelt.

Die Einführung neuer Funktionsblöcke ist ebenfalls nicht schwierig, aber es ist wichtig, hier ein Gleichgewicht zu finden. Was bringt ein Dutzend ALU-Blöcke, wenn der Mikrokreis keine Befehle an den Förderer mit einer solchen Geschwindigkeit senden kann, dass er alle diese Blöcke laden kann?

Ein Förderer mit einer erhöhten Anzahl von Sprossen, der Aufgaben in kleinere Segmente aufteilen und in kurzen Zeiträumen verarbeiten kann, erhöht einerseits die Produktivität, andererseits die negativen Folgen von falsch vorhergesagten Übergängen, Cache-Fehlern, Unterbrechungen und anderen Ereignissen, die den normalen Fluss stören Verarbeitungsbefehle im Prozessor. Um die Fähigkeiten der erweiterten Pipeline voll auszuschöpfen, muss außerdem die Taktrate erhöht werden, was bekanntlich zu einem erhöhten Stromverbrauch und einer erhöhten Wärmeableitung führt.

Schließlich können Sie Multithreading implementieren. Der Vorteil dieser Technologie besteht darin, dass ein zusätzlicher Software-Thread eingeführt wird, um Hardwareressourcen zu aktivieren, die ansonsten inaktiv wären. Basierend auf den Ergebnissen experimenteller Studien stellten Intel-Entwickler fest, dass eine Vergrößerung der Chipfläche um 5% bei der Implementierung von Multithreading für viele Anwendungen einen Leistungsgewinn von 25% ergibt. Der erste Intel-Prozessor, der Multithreading unterstützt, war der 2002er Heon. Anschließend wurde ab 3,06 GHz Multithreading in die Pentium 4-Reihe eingeführt. Intel nennt die Implementierung von Multithreading im Pentium 4-Hyperthreading.

DIE KLINGEL

Es gibt diejenigen, die diese Nachrichten vor Ihnen lesen.
Abonnieren Sie, um die neuesten Artikel zu erhalten.
Email
Name
Nachname
Wie willst du The Bell lesen?
Kein Spam