Hintergrundinformationen
Ich habe eine verteilte Verarbeitungsanwendung, die Daten analysiert. Es wurde entwickelt, um viele Datensätze in Echtzeit parallel zu verarbeiten. Als Teil des Designs wurde die Analyse in analytische Knoten aufgeteilt. Jeder Knoten nimmt Quelldaten und verarbeitet sie, um andere Daten zu erzeugen, die dann wiederum von anderen Knoten verwendet werden können. Um unsere aktuelle vollständige Analyse für einen Datensatz durchzuführen, sind etwa 200 Knoten erforderlich.
Im aktuellen Design wird jeder Knoten mit einem eigenen Thread ausgeführt. Jetzt sind diese Threads die meiste Zeit schlafend. Sie wachen jedes Mal wie ein Wasserfall auf, wenn Daten aktualisiert werden, und dann gehen sie wieder schlafen. Die Anwendung wird derzeit in der Produktion mit 40 Datensätzen ausgeführt, die jeweils 200 Knoten erfordern und 8000 Threads verwenden. Wenn keine Daten eingehen, wird der Server nicht belastet. Wenn die Daten zu den geschäftigsten Zeiten eintreffen, erreicht der Server eine CPU-Auslastung von etwa 25%. Dies alles liegt innerhalb der Entwurfs- und Produktionsparameter des Projekts.
Jetzt für den nächsten Schritt, skalieren wir die 40 Datensätze auf 200. Jeder Satz benötigt 200 Knoten, was insgesamt 40000 Knoten bedeutet, was 40000 Threads ist. Dies übersteigt die maximale PID unseres Servers. Daher habe ich von unseren Serveradministratoren verlangt, die Obergrenze zu erhöhen. Sie haben es getan, und die Anwendung funktioniert, aber sie gaben mir ein paar Rückmeldungen über die Anzahl der Threads. Ich leugne nicht, dass die Anzahl der Threads ungewöhnlich ist, aber es wird von dieser Phase unseres Designs erwartet und gerechtfertigt.
Ich plane ein paar kleine Änderungen am Design, um den Thread vom Knoten zu trennen. Auf diese Weise können wir einen Thread für die Ausführung mehrerer Knoten konfigurieren und die Anzahl der Threads reduzieren. Bei Datensätzen, die nicht häufig aktualisiert werden, ist der Leistungseffekt sehr gering, wenn ein Thread die Datenaktualisierungen in jedem Knoten ausführt. Bei Datensätzen, die hunderte Male pro Sekunde aktualisiert werden, können wir jeden Knoten so konfigurieren, dass er auf einem eigenen Thread ausgeführt wird. Tatsächlich bezweifle ich nicht, dass diese Designänderung gemacht wird - es ist nur eine Frage von wann. In der Zwischenzeit hätte ich gerne so viele Informationen wie möglich über die Konsequenzen der Verwendung dieses Designs.
Frage
Was kostet das Laufen mit über 40.000 Threads auf einer Maschine? Wie viel Leistung verliere ich, wenn das JVM / Linux-Betriebssystem diese vielen Threads verwaltet? Bitte denken Sie daran, dass sie alle richtig konfiguriert sind, um zu schlafen, wenn es keine Arbeit gibt. Also, ich spreche nur über zusätzliche Overhead und Probleme, die durch die schiere Anzahl der Threads verursacht werden.
Bitte beachten - Ich weiß, dass ich die Anzahl der Threads reduzieren kann, und ich weiß, dass es eine gute Idee ist, dieses Design zu ändern. Ich werde es so schnell wie möglich tun, aber es muss mit anderen Überlegungen zu Arbeit und Design abgewogen werden. Ich stelle diese Frage, um Informationen zu sammeln, um eine gute Entscheidung zu treffen. Ihre Gedanken und Kommentare zu dieser Natur werden sehr geschätzt.
Was kostet das Laufen mit über 40.000 Threads auf einer Maschine? Wie viel Leistung verliere ich, wenn das JVM / Linux-Betriebssystem diese vielen Threads verwaltet? Bitte denken Sie daran, dass sie alle richtig konfiguriert sind, um zu schlafen, wenn es keine Arbeit gibt. Also, ich spreche nur über zusätzliche Overhead und Probleme, die durch die schiere Anzahl der Threads verursacht werden.
Im JVM-Bereich benötigt jeder Thread einen Thread-Stack (Standard 256 KB) und das Thread-Objekt und verbundene Objekte. Der Standard-Thread-Stack kann mit der Option -Xss geändert werden, aber ich glaube das 64kb ist die untere Grenze. (40.000 x 256kb ist 10 GB ...)
Unter Linux belegt jeder Thread auch einen OS-Thread-Deskriptor, der dem Registerkontext des Threads hilft, wenn der Thread nicht ausgeführt wird ... und anderen Dingen. Diese Deskriptoren sind vorab zugewiesen, und ich glaube, sie werden nicht ausgelagert. Dies ist die Ressource, die deine Admins erhöhen mussten.
Diese Ressourcen werden verwendet, unabhängig davon, ob der Thread aktiv oder inaktiv ist.
Ein weiteres Problem ist, dass Sie bei der Synchronisierung mit wait / notifyAll vorsichtig sein müssen. Wenn es viele Threads gibt, die auf dasselbe Objekt warten, wird ein notifyAll eine Menge an Aktivitäten verursachen, wenn jeder Thread geweckt wird. (Sie können dies jedoch vermeiden, indem Sie nicht viele Threads auf dasselbe Objekt warten lassen.)
Weitere Informationen zu den Folgen der Verwendung einer großen Anzahl von Dateien finden Sie auf der Seite Oracle Threading Themen.
Mein Gefühl ist, dass 40.000 Fäden übertrieben sind. Die ideale Anzahl von Threads ist proportional zur Anzahl der physischen Prozessoren / Kerne, die Sie haben. Sie werden zwar nicht unbedingt eine Leistungsminderung durch eine große Anzahl von Threads feststellen, aber Sie werden viele Ressourcen binden, was zu indirekten Leistungsproblemen führen kann. z.B. längere GC-Zeiten, potentielles VM-Thrashing.
Eine bessere Architektur für Ihre Anwendung wäre die Implementierung eines Thread-Pools und von Arbeitswarteschlangen, um das Workout auf eine viel kleinere Anzahl aktiver Threads zu verteilen.
Nun hast du gesagt, dass Threads schlafen werden, wenn es keine Arbeit gibt. Wie oft wird es Arbeit geben? Wie viele Arbeitseinheiten werden gleichzeitig ausgeführt? Wenn diese Anzahl größer als die Anzahl der Prozessoren ist und die angegebene Arbeit hauptsächlich CPU-basiert ist, wird die Gesamtleistungsabnahme tatsächlich beeinträchtigt.
Nehmen wir jedoch an, dass die Anzahl der zu einem gegebenen Zeitpunkt ausgeführten Arbeiten die Anzahl der Prozessoren ist. Wenn das der Fall ist, ist das größte Problem, das ich sehen kann, die Menge der Kontextwechsel, die auftreten wird. Ein Kontextwechsel in Java (allgemein basiert) ist ungefähr 100 Anweisungen. Wenn alle Ihre Threads in einem kurzen Zeitraum eingeschaltet werden (aufwachen), um etwas von ihrer Arbeit zu tun, dann sprechen wir & gt; 4.000.000 zusätzliche Anweisungen.
Ein bisschen mehr Informationen über die Kosten von Kontextwechsel, da sie wahrscheinlich Ihr Programm mehr als alles andere beeinflussen werden. Ein Auszug aus diesem Dokument erklärt die Kosten für die Validierung des lokaler Cache des Threads beim Einschalten
Wenn ein neuer Thread eingeschaltet wird, wird der Daten, die es braucht, sind wahrscheinlich nicht in der lokaler Prozessor-Cache, also ein Kontext Schalter verursacht einen Wirbel von Cache vermisst, und so laufen Threads ein wenig langsamer, wenn sie zuerst sind geplant. Dies ist einer der Gründe Diese Scheduler geben jede ausführbar fädle ein bestimmtes minimales Zeitquantum auch wenn viele andere Threads sind warten
Abgesehen davon haben Sie den hinzugefügten Stapelspeicherplatz, der zugewiesen werden muss, sowie einen Heapspeicher für die 40.000 Thread-Objekte (was nur etwa 7 Megapixel flachen Heap für die Threads ist).
Tags und Links java optimization multithreading performance