Nebenläufigkeitsparadigmen und ihre Auswirkungen auf Laufzeiteigenschaften Link zu Überschrift


Abstract Link zu Überschrift

Moderne Serveranwendungen müssen zehntausende gleichzeitige Verbindungen bedienen, von denen ein großer Teil überwiegend auf externe Systeme wartet. Wie eine Laufzeitumgebung diese Nebenläufigkeit repräsentiert und auf das Betriebssystem sowie die darunterliegende Hardware abbildet, entscheidet damit unmittelbar über Skalierbarkeit, Ressourcenverbrauch und Antwortverhalten. Zwei Antworten auf dieses Skalierungsproblem stehen sich gegenüber. Das ereignisgetriebene Modell verlangt eine explizit asynchrone Strukturierung des Programmcodes, während das User-Space-Scheduling die Verwaltung der Wartesituationen in die Laufzeitumgebung verlagert und das klassische blockierende Programmiermodell erhält.

Die vorliegende Arbeit untersucht, ob folgende Modelle ihr Versprechen auf der Ebene messbarer Systemeffekte halten können. Verglichen werden vier Laufzeitsysteme, die drei Modellklassen abdecken, nämlich das Thread-per-Request-Modell der Java-Plattform-Threads, das ereignisgetriebene Modell von Node.js sowie das M:N-Scheduling der Go-Runtime und der Java Virtual Threads. Der Fokus liegt bewusst nicht auf Durchsatz und Latenz auf Anwendungsebene, sondern auf den zugrundeliegenden Mechanismen wie Kontextwechseln, Thread-Migrationen zwischen Kernen, Cache-Miss-Raten und Scheduling-Overhead. Für die Untersuchung wurde eine reproduzierbare Messumgebung auf einem dedizierten Zwei-Sockel-Server mit NUMA-Architektur aufgebaut. Vier synthetische Workloads bilden eine rechengebundene, eine dateibasierte, eine gemischte und eine netzwerkbasierte Last ab. Die Erhebung kombiniert Hardware- und Betriebssystemzähler der perf-Familie mit prozessinternen Kennzahlen aus dem /proc-Dateisystem, sodass jede Kennzahl nach Möglichkeit aus mehreren Quellen bestätigt wird. Die Ergebnisse zeigen deutliche strukturelle Unterschiede. Die Plattform-Threads erzeugen unter hoher Nebenläufigkeit die mit Abstand höchsten Kosten, sichtbar an den meisten Kontextwechseln, der höchsten Scheduling-Verzögerung, den stärksten Belastungen von Cache und TLB sowie dem größten Speicherbedarf. Go und die Virtual Threads halten die Zahl der Betriebssystem-Threads dagegen nahezu konstant, wahren dadurch die Cache-Lokalität und erreichen einen deutlich höheren Durchsatz. Node.js nimmt eine Sonderstellung ein. Auf einen einzelnen Thread bezogen ist es bei der netzwerkbasierten Last das wirksamste Modell, während seine fehlende Parallelität den Durchsatz bei rechengebundenen und gemischten Lasten begrenzt. Darüber hinaus hat sich gezeigt, dass die beobachteten Leistungsunterschiede weniger aus der reinen Zahl der Kontextwechsel als aus deren Wirkung auf die Speicherhierarchie folgen. Das User-Space-Scheduling erreicht die Leistung des ereignisgetriebenen Modells nicht durch das Einsparen der Kontextwechsel selbst, sondern durch den Erhalt der Cache-Lokalität. Bei der gemischten Last erreichen die Virtual Threads sogar den höchsten Durchsatz aller Modelle und übertreffen damit das ereignisgetriebene Modell. Für die Praxis folgt daraus eine nach der Ziellast differenzierte Empfehlung. Für I/O gebundene und gemischte Lasten bieten Virtual Threads und Goroutinen die günstigste Kombination aus hohem Durchsatz, sparsamer Cache-Nutzung und unverändertem Programmiermodell, für rein netzwerkgebundene Lasten bleibt das ereignisgetriebene Modell je Thread am effizientesten und Go erweist sich über alle Szenarien hinweg als die ausgewogenste Wahl. Damit belegt die Arbeit, dass eine systemnahe Betrachtung Ursachen sichtbar macht, die eine reine Analyse von Durchsatz und Latenz verborgen ließe.

Masterthesis