Zum Inhalt springen

Multi-Core-Programmierung für die Softwarearchitektur

Randy spricht über das Problem mit der Multi-Core-Softwarearchitektur und wie dieses Problem durch Multicore-Programmierung gelöst werden kann.

Fast jedes heute verwendete gängige Softwaresystem wurde ursprünglich vor dem Aufkommen von Multi-Core-Computern entwickelt. Multiprozessor-Computer gibt es schon seit einiger Zeit, aber nicht für den normalen Computer, und nur sehr wenige Softwaresysteme wurden angepasst, um sie voll auszunutzen.

 Mehr Hardware vs. Multi-Core-Programmierung

Warum können Sie nicht mehr Hardware darauf werfen und erwarten, dass es schneller läuft? Das Problem wird fast immer als "gemeinsam genutzte Ressourcen" bezeichnet. Eine gemeinsam genutzte Ressource ist alles, was eine Aufgabe oder ein Thread verwenden muss, ohne sich Sorgen machen zu müssen, dass eine andere Aufgabe sie ändert, während sie verwendet wird. Konzepte für Synchronisation, Sperren, gegenseitigen Ausschluss oder "kritische Abschnitte" wurden entwickelt, um das Problem der sicheren gemeinsamen Nutzung gemeinsamer Ressourcen zu lösen.

Die traditionellen Mechanismen zum Erstellen einer sicheren Freigabe zwischen Aufgaben werden als Semaphore oder Warteschlangen oder Ereignisse bezeichnet. Bei korrekter Verwendung ermöglichen die Multicore-Programmierprimitive alles, vom Inkrementieren einer gemeinsam genutzten Nummer bis zur vollständigen Datenbanktransaktion, ohne die Beschädigung, die auftreten würde, wenn Aufgaben nicht ordnungsgemäß sequenziert oder "serialisiert" würden.

Im Zeitalter der einzelnen CPUs gab es Multi-Processing oder Multi-Threading, um die Illusion zu erzeugen, dass mehrere Aufgaben gleichzeitig ausgeführt werden. Wenn Synchronisationsprimitive verhinderten, dass eine Aufgabe Fortschritte machte, war dies kein Problem, da die einzelne CPU zu einer anderen Aufgabe wechselte, die nicht blockiert war. Softwaresysteme mit gemeinsam genutzten Ressourcen wurden durch die Synchronisierung nicht zu stark beeinträchtigt, wenn die CPU immer etwas zu tun hatte.

Beispiel | Mehrere Kerne

Warum können mehrere Kerne dies nicht einfach zwei- oder viermal schneller machen? Ich werde versuchen, eine Analogie zu verwenden, um es zu erklären. Stellen Sie sich eine Person an einem Schreibtisch mit einem Posteingang, einem Computer und einem Postausgang vor. Die Aufgabe besteht darin, den nächsten Artikel aus dem Posteingang zu nehmen und zu lesen, Informationen aus dem Artikel in den Computer einzugeben, eine Antwort vom Computer zu erhalten, sie auf ein Formular zu schreiben, für die Zustellung vorzubereiten und in den Postausgang zu legen. Dieser Zyklus dauert durchschnittlich 5 Minuten pro Gegenstand, und der Chef benötigt eine Ausgabe von 30 pro Stunde, nicht die aktuelle 12. Um dieses Problem zu lösen, setzt der Chef eine andere Person (Kern) an denselben Schreibtisch und lässt den Computer schwenken . Als erstes greifen beide Personen in den Posteingang, greifen nach demselben Gegenstand und zerreißen ihn in zwei Teile. Von vorne anfangen, vereinbaren sie, den anderen zu benachrichtigen, bevor sie greifen. Jetzt lesen sie jeweils ihre nächsten Elemente und sind zur Dateneingabe bereit. Leider kann dies nur einer auf einmal tun, so dass der andere wartet, den Monitor umdreht und mit der Dateneingabe beginnt. In der Zwischenzeit bereitete die erste Person die Antwort für den Postausgang vor, griff nach dem nächsten Artikel und musste eine kurze Zeit auf den Computermonitor warten.

 Das Problem

Die gute Nachricht ist, dass die Ausgabe von 12 Artikeln pro Stunde auf 18 gestiegen ist. Die schlechte Nachricht ist, dass der Chef immer noch 30 benötigt. Eine weitere Person wird zum Schreibtisch hinzugefügt und die Produktion steigt von 18 auf 22 Artikel pro Stunde. Die nächste Person nimmt es auf 24. Eine weitere Person verschiebt es auf 22, da diese Person, die gleichermaßen gut in der Verarbeitung von Gegenständen ist, tatsächlich die bereits geleistete Arbeit stört.

So geht es auf sehr reale Weise mit Softwaresystemen, auf die mehr Kerne geworfen werden. Die ersten paar helfen ein wenig, aber sie kommen zu einem Punkt, an dem sich die Leistung verschlechtert.

Die Lösung

Wenn stattdessen ein anderer Schreibtisch, Posteingang, Computer und Postausgang für die zweite Person bereitgestellt würden, würde sich die Ausgabe fast verdoppeln. Fast, weil zusätzliche Zeit erforderlich wäre, um zwei Posteingänge zu füllen und zwei Postausgänge zu leeren, anstatt nur einen, aber eindeutig ein guter Kompromiss. Die Lösung ist teurer, macht sich aber schnell bezahlt.

Das Ändern eines Softwaresystems, um die Arbeit aufzuteilen, ist nicht so einfach, insbesondere Software, die gut etabliert und ausgereift ist. Es ist nicht nur eine Änderung der Algorithmen, sondern auch eine Änderung der Architektur.

Wenn Sie eine Websuche nach Multi-Core-Optimierung durchführen, finden Sie Artikel über gemeinsam genutzter L2-Cache oder Matrixverarbeitung. Viele Anbieter von Betriebssystemen und Chips geben an, dass sie die Optimierung für Sie durchgeführt haben, aber sie sagen nicht, wie. Einige haben Programmiersprachen vorgeschlagen, denen Parallelverarbeitungskonstrukte hinzugefügt wurden. All dies ist in Ordnung und hilfreich, aber beheben Sie ein Architekturproblem nicht mehr, als es den Personen, die einen einzelnen Posteingang verarbeiten, hilft, sie an eine Schnellleseklasse zu senden. Das schnelle Lesen ist großartig, aber ohne eine neue Schreibtischarchitektur hat es nur sehr begrenzte Vorteile.

Es gibt nur sehr wenige Artikel über die System- und Multicore-Programmierarchitektur, in denen die Hauptvorteile der systemweiten Leistung erörtert werden. Es stellt sich heraus, dass es nur ein Hauptentwurfsprinzip für die Erstellung eines Softwaresystems gibt, das mit mehreren Kernen skaliert werden kann:

Besorgen Sie sich alle Ressourcen, die für eine Aufgabe benötigt werden

Schritt eins ist am besten, wenn es nicht blockiert. Das nächstbeste ist, es sehr detailliert zu gestalten, was bedeutet, dass eine Aufgabe Teile einer Ressource enthalten kann, während eine andere andere Teile enthält.

Führen Sie die Aufgabe aus

Schritt zwei mag zeitaufwändig sein, blockiert jedoch in einer korrekten Multicore-Programmierarchitektur keine anderen Aufgaben. Es kann parallel ausgeführt werden. Ein wichtiges Prinzip von Schritt zwei ist, dass die Summe der für alle Kerne benötigten Zeit viel mehr sein kann als die Zeit, die eine CPU benötigen würde, aber die Wanduhrzeit ist viel kürzer, weil sie gleichzeitig ausgeführt werden. Wenn beispielsweise Schritt zwei ein Einzel-CPU-System 100 ms benötigt, würden zwölf Aufgaben 1200 ms dauern. In einem neu strukturierten Mehrkernsystem mit 4 Kernen, die 180 ms für Schritt zwei benötigen, würden 4 parallele Kerne, die die Aufgabe jeweils dreimal ausführen (zwölf Aufgaben), in 540 ms ausgeführt. Die Zeit wurde nicht um 4 verkürzt, aber die Bühne für eine echte Skalierung ist bereit, wenn noch mehr Kerne hinzugefügt werden.

 Führen Sie die Ergebnisse der Aufgabe schnell zusammen oder integrieren Sie sie erneut

Der dritte Schritt ist schwer, besonders für a Datenbankverwaltungssystem, wo ACID-Prinzipien befolgt werden müssen. Das sichere und dauerhafte Speichern von Transaktionen auf der Festplatte erfordert eine Synchronisierung der gemeinsam genutzten Ressourcen. Der Schlüssel hier ist, so viel Arbeit wie möglich im Voraus zu erledigen, damit die Wiedereingliederung schnell erfolgt. Dies ist wahrscheinlich der Grund, warum Schritt zwei länger dauert. Schritt zwei erledigt mehr Arbeit (parallel), so dass der serialisierte Teil kurz ist. Es ist ein äußerst lohnender Kompromiss.

Der Trick ist jetzt zu Nehmen Sie funktionierende Software und erstellen Sie sie neu, nicht nur optimieren, für Multi-Core-Skalierung.