Künstliche Intelligenz
SGLang: Effiziente Ausführung strukturierter Sprachmodellprogramme
Große Sprachmodelle (LLMs) werden zunehmend für komplexe Aufgaben eingesetzt, die mehrere Generierungsaufrufe, erweiterte Eingabeaufforderungstechniken, Kontrollfluss und strukturierte Ein-/Ausgaben erfordern. Es fehlen jedoch effiziente Systeme zum Programmieren und Ausführen dieser Anwendungen. SGLang, ein neu eingeführtes System, soll dieses Problem lösen, indem es eine effiziente Ausführung komplexer Sprachmodellprogramme ermöglicht. SGLang besteht aus einer Frontend-Sprache und einer Laufzeitumgebung. Das Frontend vereinfacht die Programmierung mit Primitiven für die Generierung und Parallelitätssteuerung, während die Laufzeit die Ausführung durch neuartige Optimierungen wie RadixAttention für die Wiederverwendung des KV-Cache und komprimierte Finite-State-Maschinen für eine schnellere Dekodierung strukturierter Ausgaben beschleunigt. Experimente zeigen, dass SGLang im Vergleich zu hochmodernen Inferenzsystemen bei verschiedenen großen Sprach- und multimodalen Modellen einen bis zu 6.4-fach höheren Durchsatz erreicht und Aufgaben wie Agentensteuerung, logisches Denken, Benchmarks für Few-Shot-Learning, JSON-Dekodierung, durch Abruf erweiterte Generierungspipelines und Multi-Turn-Chat bewältigt.
Jüngste Fortschritte bei den LLM-Funktionen haben ihren Nutzen erweitert und ermöglichen es ihnen, eine größere Bandbreite allgemeiner Aufgaben zu bewältigen und als autonome Agenten zu fungieren. In diesen Anwendungen beteiligen sich LLMs an mehrstufiger Planung, Argumentation und Interaktion mit externen Umgebungen. Dies wird durch die Verwendung von Tools, mehreren Eingabemodalitäten und verschiedenen Eingabetechniken wie Lernen mit wenigen Versuchen, Selbstkonsistenz, Gedankenskelett und Gedankenbaum erleichtert. Diese neuen Anwendungsfälle erfordern mehrere, oft voneinander abhängige LLM-Generierungsaufrufe, was auf einen Trend hindeutet, Strukturen mit mehreren Aufrufen zur Erledigung komplexer Aufgaben zu verwenden.
Dieser Wandel markiert einen Übergang vom einfachen Chatten zu einer anspruchsvolleren programmatischen Nutzung von LLMs, bei der Programme die Generierungsprozesse von LLMs planen und steuern. Diese Programme werden als „Language Model Programs“ (LM-Programme) bezeichnet. Fortgeschrittene Eingabeaufforderungstechniken und agentenbasierte Workflows fallen in den Anwendungsbereich von LM-Programmen. LM-Programme haben zwei gemeinsame Eigenschaften: (1) LM-Programme beinhalten typischerweise mehrere LLM-Aufrufe, die mit Kontrollfluss durchsetzt sind, um komplexe Aufgaben zu erledigen und die Gesamtqualität zu verbessern. (2) LM-Programme erhalten strukturierte Eingaben und erzeugen strukturierte Ausgaben, wodurch die Zusammenstellung von LM-Programmen und die Integration in bestehende Softwaresysteme ermöglicht wird.
In diesem Artikel werden wir tiefer in das SGLang-Framework eintauchen, seine Architektur untersuchen, seine Leistung analysieren und es mit den modernsten Frameworks vergleichen. Also, legen wir los.
Eine Einführung in SGLang
Trotz der weit verbreiteten Verwendung von LM-Programmen sind die aktuellen Systeme zu ihrer Darstellung und Ausführung nach wie vor ineffizient. SGLang identifiziert zwei Hauptherausforderungen im Zusammenhang mit der effizienten Verwendung von LM-Programmen:
- Programmierkomplexität: Die Entwicklung von LM-Programmen ist aufgrund der nichtdeterministischen Natur von LLMs mühsam und schwierig. Dies erfordert umfangreiche Zeichenfolgenmanipulation, experimentelle Feinabstimmung von Eingabeaufforderungen, fehlerhafte Ausgabeanalyse, die Handhabung mehrerer Eingabemodalitäten und die Implementierung von Parallelitätsmechanismen. Diese Komplexität verringert die Lesbarkeit selbst einfacher Programme erheblich.
- Ineffiziente Ausführung: Die Ausführung von LM-Programmen ist aufgrund redundanter Berechnungen und Speichernutzung ineffizient. Moderne Inferenzmaschinen, die auf Latenzreduzierung und Durchsatzoptimierung optimiert sind, verfügen nicht über direkte Kenntnisse der Arbeitslast, was zu erheblichen Ineffizienzen führt. Ein bemerkenswertes Beispiel ist die Wiederverwendung des Key-Value-Cache (KV-Cache), der aus wiederverwendbaren Zwischentensoren besteht, die für die generative Inferenz unerlässlich sind. Aktuellen Systemen fehlen wirksame Mechanismen zur Erleichterung der Wiederverwendung des KV-Cache über mehrere LLM Aufrufe mit einem gemeinsamen Präfix führen zu unnötigen Berechnungen und Speicherverschwendung. Darüber hinaus ist die eingeschränkte Dekodierung für strukturierte Ausgaben, wie etwa den JSON-Modus, nicht optimal, da vorhandene Systeme nur jeweils ein Token dekodieren.
Um diese Herausforderungen zu bewältigen, führt SGLang eine strukturierte Generierungssprache für LLMs ein. Die Kernidee besteht darin, die Multi-Call-Struktur in LM-Programmen systematisch für eine effiziente Ausführung auszunutzen. Wie in der folgenden Abbildung dargestellt, besteht SGLang aus zwei Teilen: einer Front-End-Sprache und einer Back-End-Laufzeit.

Das Frontend vereinfacht die Programmierung von LM-Programmen und die Runtime beschleunigt deren Ausführung. Diese Teile können für eine bessere Leistung zusammenarbeiten oder unabhängig voneinander funktionieren.
SGLang ist eine domänenspezifische Sprache, die in Python eingebettet ist und Grundelemente für die Generierung (z. B. Erweitern, Erzeugen, Auswählen) und Parallelitätssteuerung (z. B. Fork, Join) bereitstellt. Es ist mit dem Kontrollfluss und den Bibliotheken von Python kompatibel, sodass Benutzer problemlos erweiterte Eingabeaufforderungs-Workflows mit nativer Python-Syntax entwickeln können. SGLang enthält einen Interpreter und einen Compiler. Der Interpreter verwaltet den Eingabeaufforderungsstatus als Stream und übergibt Grundoperationen zur asynchronen Ausführung an den Stream, wodurch eine ordnungsgemäße Kontrolle über die Synchronisierung und die Intraprogramm-Parallelität gewährleistet wird. Darüber hinaus können SGLang-Programme zur weiteren Optimierung verfolgt und kompiliert werden. Die Laufzeit von SGLang bietet mehrere neuartige Optimierungen zur Beschleunigung der Ausführung von LM-Programmen:
- RadixAttention: Diese Technik ermöglicht die automatische Wiederverwendung des KV-Caches über mehrere Generierungsaufrufe hinweg. In vorhandenen Inferenzmaschinen wird der KV-Cache einer Anfrage nach der Verarbeitung verworfen, was die Wiederverwendung über mehrere Aufrufe hinweg verhindert und die Ausführung verlangsamt. SGLang verwaltet einen LRU-Cache des KV-Caches innerhalb eines Radixbaums, verwaltet den KV-Cache als herkömmlichen Cache und verwendet den Radixbaum für effizientes Matching, Einfügen und Entfernen. Dadurch kann die Laufzeitumgebung verschiedene Wiederverwendungsmuster effizient handhaben.
- Komprimierte Finite-State-Maschine: Diese Technik ermöglicht eine schnellere eingeschränkte Dekodierung strukturierter Ausgaben. Vorhandene Systeme befolgen Einschränkungen nur für das nächste Token, sodass sie jeweils nur ein Token dekodieren können. Stattdessen analysiert SGLang die Einschränkungen und erstellt eine komprimierte Finite-State-Maschine, um sie darzustellen. Dabei wird ein Pfad mit mehreren Token, wann immer möglich, in einen Pfad mit einem einzigen Schritt komprimiert, sodass mehrere Token gleichzeitig dekodiert werden können, was eine höhere Geschwindigkeit ermöglicht.
- API Speculative Execution: Für API-only-Modelle wie GPA-4 von OpenAI, SGLang führt die spekulative API-Ausführung ein, um Programme mit mehreren Aufrufen zu optimieren.
Mithilfe von SGLang wurden verschiedene LLM-Anwendungen implementiert, darunter Agentensteuerung, logisches Denken, Benchmarks für Few-Shot-Learning, JSON-Dekodierung, Retrieval-Augmented Generation Pipelines, Multi-Turn-Chat und Multimodalitätsverarbeitung. Die Leistung wurde an Modellen wie Llama-7B/70B, Mistral-8x7B, LLaVA-v1.5-7B (Bild) und LLaVA-NeXT-34B (Video) auf NVIDIA A10G- und A100-GPUs getestet. Versuchsergebnisse zeigen, dass SGLang im Vergleich zu bestehenden Programmier- und Inferenzsystemen wie Guidance, vLLM und LMQL bei einer Vielzahl von Workloads, Modellen und Hardware-Setups einen bis zu 6.4-fach höheren Durchsatz erreicht.
SGLang: Programmiermodell und Methodik
Das SGLang-Programmiermodell wird anhand eines laufenden Beispiels vorgestellt, wobei die Sprachprimitive und Ausführungsmodi beschrieben und Möglichkeiten zur Laufzeitoptimierung aufgezeigt werden. Dieses Modell vereinfacht mühsame Vorgänge in Workflows mit mehreren Aufrufen (z. B. Zeichenfolgenmanipulation, API-Aufruf, Einschränkungsspezifikation, Parallelität), indem es flexible und zusammensetzbare Primitive bereitstellt. SGLang ist eine domänenspezifische Sprache, die in Python eingebettet ist. Die folgende Abbildung zeigt ein Programm, das einen Aufsatz über ein Bild mithilfe der Branch-Solve-Merge-Eingabeaufforderungsmethode auswertet.

Die Funktion mehrdimensionaler Richter nimmt drei Argumente an: `s`, `Pfad` und `Aufsatz`. s verwaltet den Eingabeaufforderungsstatus, path ist der Bilddateipfad und essay ist der Essaytext. Neue Zeichenfolgen und SGLang-Primitive können an den Status s zur Ausführung angehängt werden, indem der += Operator. Zuerst fügt die Funktion das Bild und den Aufsatz zur Eingabeaufforderung hinzu. Anschließend prüft sie, ob der Aufsatz mit dem Bild in Zusammenhang steht, indem sie select verwendet, und speichert das Ergebnis in s["verwandt"]. Wenn verwandt, wird die Eingabeaufforderung in drei Kopien verzweigt, um sie parallel aus verschiedenen Dimensionen auszuwerten. Dabei wird gen verwendet, um die Ergebnisse in f[„Urteil“]. Anschließend werden die Urteile zusammengeführt, eine Zusammenfassung erstellt und eine Note vergeben. Abschließend werden die Ergebnisse im JSON-Format zurückgegeben, wobei ein Schema befolgt wird, das durch eine reguläre Ausdrucksbeschränkung definiert ist. Regex. SGLang vereinfacht dieses Programm erheblich, da ein entsprechendes Programm mit einer OpenAI API-ähnlichen Schnittstelle aufgrund der manuellen Zeichenfolgenmanipulation und Parallelitätskontrolle 2.1-mal so viele Codezeilen benötigen würde.
SGLang bietet Primitive zur Steuerung des Eingabeaufforderungsstatus, der Generierung und der Parallelität, die mit Python-Syntax und -Bibliotheken verwendet werden können. Hier sind die Primitiven:
Genre: Ruft ein zu generierendes Modell auf und speichert die Ergebnisse in einer Variable mit dem im ersten Argument angegebenen Namen. Es unterstützt ein „regex“-Argument, um die Ausgabe so einzuschränken, dass sie einer durch einen regulären Ausdruck definierten Grammatik folgt (z. B. einem JSON-Schema).
- select: Ruft ein Modell auf, um aus einer Liste die Option mit der höchsten Wahrscheinlichkeit auszuwählen.
- += oder erweitern: Fügt der Eingabeaufforderung eine Zeichenfolge hinzu.
- [variable_name]: Ruft die Ergebnisse einer Generierung ab.
- fork: Erstellt parallele Forks des Prompt-Status.
- beitreten: Tritt dem Eingabeaufforderungsstatus wieder bei.
- Bild und Video: Nehmen Sie Bild- und Videoeingaben auf.
Der einfachste Weg, ein SGLang-Programm auszuführen, ist über einen Interpreter, bei dem eine Eingabeaufforderung als asynchroner Datenstrom behandelt wird. Primitive wie Erweitern, Generieren und Auswählen werden zur asynchronen Ausführung an den Stream übermittelt. Diese nicht blockierenden Aufrufe ermöglichen es, Python-Code weiter auszuführen, ohne auf die Fertigstellung der Generierung warten zu müssen, ähnlich wie beim asynchronen Starten von CUDA-Kerneln. Jede Eingabeaufforderung wird von einem Stream-Executor in einem Hintergrundthread verwaltet, was eine Intraprogramm-Parallelität ermöglicht. Das Abrufen der Generierungsergebnisse wird blockiert, bis sie fertig sind, wodurch eine korrekte Synchronisierung sichergestellt wird. Alternativ können SGLang-Programme als Berechnungsgraphen kompiliert und mit einem Graph-Executor ausgeführt werden, was weitere Optimierungen ermöglicht. Dieses Dokument verwendet standardmäßig den Interpretermodus und erörtert die Ergebnisse des Compilermodus in Anhang D. SGLang unterstützt Open-Weight-Modelle mit seiner eigenen SGLang Runtime (SRT) sowie API-Modelle wie OpenAI und anthropische Modelle.
Programmiersysteme für LLMs können als High-Level-Systeme (z. B. LangChain, DSPy) und Low-Level-Systeme (z. B. LMQL, Guidance, SGLang) klassifiziert werden. High-Level-Systeme bieten vordefinierte oder automatisch generierte Eingabeaufforderungen, wie z. B. der Eingabeaufforderungsoptimierer von DSPy. Low-Level-Systeme ändern Eingabeaufforderungen normalerweise nicht, ermöglichen aber die direkte Manipulation von Eingabeaufforderungen und Grundelementen. SGLang ist ein Low-Level-System ähnlich wie LMQL und Guidance. Die folgende Tabelle vergleicht ihre Funktionen.

SGLang konzentriert sich mehr auf Laufzeiteffizienz und verfügt über eine eigene, mitentwickelte Laufzeitumgebung, die neuartige Optimierungen ermöglicht. Höhere Sprachen (z. B. DSPy) können in niedrigere Sprachen (z. B. SGLang) kompiliert werden. Die Integration von SGLang als Backend in DSPy für eine bessere Laufzeiteffizienz wird später demonstriert.

Das obige Beispiel veranschaulicht RadixAttention-Operationen mit einer LRU-Räumungsrichtlinie über neun Zeitpunkte hinweg und zeigt die dynamische Entwicklung des Radix-Baums als Reaktion auf verschiedene Anfragen. Diese Anfragen umfassen zwei Chat-Sitzungen, einen Stapel von Few-Shot-Learning-Anfragen und Selbstkonsistenz-Sampling. Jede Baumkante trägt eine Beschriftung, die eine Teilzeichenfolge oder eine Token-Sequenz bezeichnet. Die Knoten sind farbcodiert, um verschiedene Zustände widerzuspiegeln: Grün für neu hinzugefügte Knoten, Blau für zwischengespeicherte Knoten, auf die während des Zeitpunkts zugegriffen wurde, und Rot für Knoten, die geräumt wurden.
Schritt 1: Der Radixbaum ist anfangs leer.
Schritt 2: Der Server verarbeitet eine eingehende Benutzernachricht „Hallo“ und antwortet mit der LLM-Ausgabe „Hi“. Die Systemaufforderung „Sie sind ein hilfreicher Assistent“, die Benutzernachricht „Hallo!“ und die LLM-Antwort „Hi!“ werden im Baum als einzelne Kante zusammengefasst, die mit einem neuen Knoten verknüpft ist.
Schritt 3: Eine neue Eingabeaufforderung kommt an, und der Server findet das Präfix der Eingabeaufforderung (d. h. die erste Wendung der Konversation) im Radix-Baum und verwendet seinen KV-Cache erneut. Die neue Wendung wird dem Baum als neuer Knoten angehängt.
Schritt 4: Eine neue Chat-Sitzung beginnt. Der Knoten aus Schritt 3 wird in zwei Knoten aufgeteilt, damit die beiden Chat-Sitzungen die Systemeingabeaufforderung gemeinsam nutzen können.
Schritt 5: Die zweite Chat-Sitzung wird fortgesetzt. Aus Speichergründen muss jedoch ein Knoten aus Schritt 4 entfernt werden. Der neue Turn wird nach dem verbleibenden Knoten aus Schritt 4 angehängt.
Schritt 6: Der Server empfängt eine Few-Shot-Learning-Abfrage, verarbeitet sie und fügt sie in den Baum ein. Der Stammknoten wird geteilt, da die neue Abfrage kein Präfix mit vorhandenen Knoten gemeinsam hat.
Schritt 7: Der Server empfängt einen Stapel zusätzlicher Few-Shot-Learning-Abfragen. Diese Abfragen verwenden denselben Satz von Few-Shot-Beispielen, sodass ein Knoten aus Schritt 6 aufgeteilt wird, um die gemeinsame Nutzung zu ermöglichen.
Schritt 8: Der Server empfängt eine neue Nachricht aus der ersten Chat-Sitzung. Er entfernt alle Knoten aus der zweiten Chat-Sitzung, da diese am wenigsten verwendet wurden.
Schritt 9: Der Server erhält eine Anfrage, weitere Antworten auf die Fragen in einem Knoten aus Schritt 8 abzufragen, wahrscheinlich zur Selbstkonsistenzaufforderung. Um Platz für diese Anfragen zu schaffen, werden mehrere Knoten entfernt.
Dieses Beispiel zeigt, wie RadixAttention die dynamische Zuweisung und Räumung von Knoten als Reaktion auf verschiedene Arten von Anforderungen handhabt und so eine effiziente Wiederverwendung des KV-Cache und Speicherverwaltung gewährleistet.
SGLang: Auswertung und Ergebnisse
Ergebnisse zu Open-Weight-Modellen
Die Ergebnisse zu Latenz und Durchsatz sind in den folgenden Abbildungen dargestellt. SGLang verbessert den Durchsatz um das bis zu 6.4-Fache und reduziert die Latenz um das bis zu 3.7-Fache. Diese Verbesserungen sind das Ergebnis der Wiederverwendung des KV-Cache, der Ausnutzung der Parallelität innerhalb eines einzelnen Programms und einer schnelleren eingeschränkten Dekodierung.

Bei diesen Benchmarks liegt die Cache-Trefferquote zwischen 50 % und 99 %. Abbildung 13 (Anhang) listet die erreichten und optimalen Cache-Trefferquoten für alle Benchmarks auf und zeigt, dass die cachebewusste Planung von SGLang im Durchschnitt 96 % der optimalen Trefferquote erreicht.

Ergebnisse bei größeren Modellen mit Tensorparallelität
Größere Modelle, Mixtral-8x7B und Llama-70B, wurden mit Tensorparallelität anhand derselben Benchmarks getestet. Die Ergebnisse sind in der folgenden Abbildung dargestellt. Die Beschleunigung bei größeren Modellen zeigt einen ähnlichen Trend wie bei kleineren Modellen. Dies deutet darauf hin, dass die Optimierung von SGLang gut auf größere Modelle übertragbar ist. Guidance und LMQL wurden aufgrund fehlender effizienter Implementierungen von Tensorparallelität weggelassen.

Ergebnisse zu multimodalen Modellen
SGLang bietet native Unterstützung für multimodale Modelle mit den Bild- und Videoprimitiven. Die Optimierungen in diesem Dokument sind mit multimodalen Modellen kompatibel. Für RadixAttention wird der Hash der Eingabebilder berechnet und als Schlüssel im Radix-Baum verwendet, wodurch der KV-Cache der Bild-Token desselben Bildes wiederverwendet werden kann. LLaVA-v1.5-7B (Bild) wurde auf llava-bench-in-the-wild und LLaVA-NeXT-34B (Video) auf ActivityNet ausgeführt. Da diese Modelle von anderen Basissystemen nicht gut unterstützt werden, wurde die ursprüngliche Implementierung der Modellautoren in Hugging Face Transformers als Basis verwendet. Wie in der folgenden Tabelle gezeigt, bietet SGLang bei diesen Benchmarks einen bis zu 6-mal höheren Durchsatz. In llava-bench-in-the-wild wurden mehrere Fragen zum selben Bild verarbeitet, und die SGLang-Laufzeit verwendete in diesem Fall den KV-Cache wieder.

Produktionsbereitstellung
SGLang wurde in Chatbot Arena eingesetzt, um Modelle mit offenem Gewicht zu bedienen. Aufgrund des geringen Datenverkehrs für einige Modelle bedient nur ein SGLang-Worker jedes Modell. Nach einem Monat wurde eine RadixAttention-Cache-Trefferquote von 52.4 % für LLaVA-Next-34B und 74.1 % für Vicuna-33B beobachtet. Cache-Treffer kamen von allgemeinen Systemnachrichten, häufig wiederverwendeten Beispielbildern und Chat-Verläufen mit mehreren Durchläufen. Dies reduzierte die Latenz des ersten Tokens für Vicuna-1.7B im Durchschnitt um das 33-Fache.

Fazit
In diesem Artikel haben wir über SGLang gesprochen, ein neu eingeführtes System, das dieses Problem lösen soll, indem es eine effiziente Ausführung komplexer Sprachmodellprogramme ermöglicht. SGLang besteht aus einer Frontend-Sprache und einer Laufzeitumgebung. Das Frontend vereinfacht die Programmierung mit Primitiven zur Generierung und Parallelitätskontrolle, während die Laufzeitumgebung die Ausführung durch neuartige Optimierungen wie RadixAttention für die Wiederverwendung des KV-Cache und komprimierte Finite-State-Maschinen für eine schnellere strukturierte Ausgabedekodierung beschleunigt. Experimente zeigen, dass SGLang im Vergleich zu hochmodernen Inferenzsystemen bei verschiedenen großen Sprach- und multimodalen Modellen einen bis zu 6.4-fach höheren Durchsatz erreicht und Aufgaben wie Agentensteuerung, logisches Denken, Benchmarks für das Lernen mit wenigen Versuchen, JSON-Dekodierung, durch Abruf erweiterte Generierungspipelines und Multiturn-Chat bewältigt.












