Best Practices: KI-Inferenz in Cloud Run-Diensten mit GPUs

Auf dieser Seite finden Sie Best Practices zur Leistungsoptimierung bei der Verwendung eines Cloud Run-Dienstes mit GPU für die KI-Inferenz, wobei der Schwerpunkt auf großen Sprachmodellen (Large Language Models, LLMs) liegt. So erstellen und stellen Sie einen Cloud Run-Dienst bereit, der in Echtzeit auf Skalierungsereignisse reagieren kann:
  • Verwenden Sie Modelle, die schnell geladen werden und nur minimale Transformationen in GPU-fähige Strukturen erfordern. Optimieren Sie außerdem das Laden der Modelle.
  • Verwenden Sie Konfigurationen, die eine maximale, effiziente, gleichzeitige Ausführung ermöglichen, um die Anzahl der GPUs zu reduzieren, die zum Ausführen einer Zielanfrage pro Sekunde erforderlich sind, und gleichzeitig die Kosten niedrig zu halten.

Empfohlene Methoden zum Laden großer ML-Modelle in Cloud Run

Google empfiehlt, ML-Modelle aus Cloud Storage herunterzuladen und über die Google Cloud CLI darauf zuzugreifen. Alternativ können Sie Modelle in Container-Images speichern. Diese Methode eignet sich jedoch am besten für kleinere Modelle mit weniger als 10 GB.

Vor- und Nachteile beim Speichern und Laden von ML-Modellen

Hier ein Vergleich der Optionen:

Modellstandort Bereitstellungszeit Entwicklungserfahrung Startzeit des Containers Speicherkosten
Cloud Storage, gleichzeitig heruntergeladen mit dem Google Cloud CLI-Befehl gcloud storage cp oder der Cloud Storage API, wie im Codebeispiel für den gleichzeitigen Download mit dem Transfer Manager gezeigt. Am schnellsten. Modell wird beim Starten des Containers heruntergeladen. Achten Sie darauf, dass der Cloud Run-Instanz genügend RAM zugewiesen ist, um die Modelldateien zu speichern. Etwas schwieriger einzurichten, da Sie entweder die Google Cloud CLI auf dem Image installieren oder Ihren Code aktualisieren müssen, um die Cloud Storage API zu verwenden. Weitere Informationen zum Abrufen von Anmeldedaten vom Metadatenserver finden Sie unter Einführung in die Dienstidentität. Schnell bei Netzwerkoptimierungen. Die Google Cloud CLI lädt die Modelldatei parallel herunter. Eine Kopie in Cloud Storage.
Cloud Storage über das Cloud Storage FUSE-Volume bereitgestellt Höheres Tempo. Modell wird beim Starten des Containers heruntergeladen. Einfach einzurichten, keine Änderungen am Docker-Image erforderlich. Schnell bei Netzwerkoptimierungen. Eine Kopie in Cloud Storage.
Container-Image Antworten finden Das Importieren eines Images mit einem großen Modell in Cloud Run dauert länger. Sie müssen jedes Mal ein neues Image erstellen, wenn Sie ein anderes Modell verwenden möchten. Änderungen am Container-Image erfordern eine erneute Bereitstellung, was bei großen Images langsam sein kann. Abhängig von der Größe des Modells. Verwenden Sie für sehr große Modelle Cloud Storage, um eine vorhersehbarere, aber langsamere Leistung zu erzielen. Möglicherweise mehrere Kopien in Artifact Registry.
Internet Langsam. Modell wird beim Starten des Containers heruntergeladen. In der Regel einfacher (viele Frameworks laden Modelle aus zentralen Repositories herunter). In der Regel schlecht und unvorhersehbar:
  • Frameworks können während der Initialisierung Modelltransformationen anwenden. (Sie sollten dies während der Erstellung tun).
  • Modellhost und Bibliotheken zum Herunterladen des Modells sind möglicherweise nicht effizient.
  • Das Herunterladen aus dem Internet birgt Zuverlässigkeitsrisiken. Der Start Ihres Dienstes kann fehlschlagen, wenn das Downloadziel nicht erreichbar ist. Außerdem kann sich das heruntergeladene zugrunde liegende Modell ändern, was die Qualität verringert. Wir empfehlen, das Modell in Ihrem eigenen Cloud Storage-Bucket zu hosten.
Abhängig vom Anbieter des Modell-Hostings.

Modelle in Cloud Storage speichern

Wenn Sie das Laden von ML-Modellen aus Cloud Storage optimieren möchten, entweder über Cloud Storage-Volumen-Bereitstellungen oder direkt über die Storage API oder die Befehlszeile, müssen Sie Direct VPC mit der Einstellung für ausgehenden Traffic auf all-traffic und privater Google-Zugriff verwenden.

Gegen Aufpreis können Sie mit Rapid Cache die Latenz beim Laden von Modellen reduzieren, indem Daten effizient auf SSDs zwischengespeichert werden, um das Lesen zu beschleunigen.

Um die Lesezeiten für Modelle zu verkürzen, können Sie die folgenden Bereitstellungsoptionen verwenden, um Cloud Storage FUSE-Funktionen zu aktivieren:

  • cache-dir: Aktivieren Sie die Funktion für das Datei-Caching mit einer In-Memory-Volumen-Bereitstellung, die als zugrunde liegendes Verzeichnis zum Speichern von Dateien verwendet werden soll. Legen Sie den Wert der Bereitstellungsoption cache-dir auf den Namen des In-Memory-Volumes im Format cr-volume:{volume name} fest. Wenn Sie beispielsweise ein In-Memory-Volume mit dem Namen in-memory-1 haben, das Sie als Cache-Verzeichnis verwenden möchten, geben Sie cr-volume:in-memory-1 an. Wenn dieser Wert festgelegt ist, können Sie auch andere file-cache Flags festlegen, die für den Cache konfiguriert werden können.
  • enable-buffered-read: Legen Sie das enable-buffered-read-Feld auf true fest, um das asynchrone Prefetching von Teilen eines Cloud Storage-Objekts in einen Arbeitsspeicher-Puffer zu aktivieren. Dadurch können nachfolgende Lesevorgänge aus dem Puffer bedient werden, ohne dass Netzwerkaufrufe erforderlich sind. Wenn Sie dieses Feld konfigurieren, können Sie auch das read-global-max-blocks Feld festlegen, um die maximale Anzahl an Blöcken zu konfigurieren, die für gepufferte Lesevorgänge für alle Datei-Handles verfügbar sind.

Wenn sowohl cache-dir als auch enable-buffered-read verwendet werden, hat cache-dir Vorrang. Beachten Sie, dass die Aktivierung einer dieser Funktionen die Ressourcenabrechnung des Cloud Storage FUSE-Prozesses ändert, sodass sie unter den Arbeitsspeicherlimits des Containers gezählt wird. Sie können das Arbeitsspeicherlimit des Containers erhöhen. Folgen Sie dazu der Anleitung zum Konfigurieren von Arbeitsspeicherlimits.

Modelle in Container-Images speichern

Wenn Sie das ML-Modell im Container-Image speichern, profitiert das Laden des Modells von der optimierten Container-Streaming-Infrastruktur von Cloud Run. Das Erstellen von Container-Images, die ML-Modelle enthalten, ist jedoch ein ressourcenintensiver Prozess, insbesondere bei großen Modellen. Insbesondere der Build-Prozess kann durch den Netzwerkdurchsatz begrenzt werden. Wenn Sie Cloud Build verwenden, empfehlen wir eine leistungsstärkere Build-Maschine mit erhöhter Rechen- und Netzwerkleistung. Erstellen Sie dazu ein Image mit einer Build-Konfigurationsdatei, die die folgenden Schritte enthält:

steps:
- name: 'gcr.io/cloud-builders/docker'
  args: ['build', '-t', 'IMAGE', '.']
- name: 'gcr.io/cloud-builders/docker'
  args: ['push', 'IMAGE']
images:
- IMAGE
options:
 machineType: 'E2_HIGHCPU_32'
 diskSizeGb: '500'
 

Sie können eine Modellkopie pro Image erstellen, wenn sich die Ebene mit dem Modell zwischen den Images unterscheidet (unterschiedlicher Hash). Es können zusätzliche Kosten für Artifact Registry anfallen, da es eine Kopie des Modells pro Image geben kann, wenn die Modellebene für jedes Image eindeutig ist.

Modelle aus dem Internet laden

Um das Laden von ML-Modellen aus dem Internet zu optimieren, leiten Sie den gesamten Traffic über das VPC-Netzwerk und legen Sie für die Einstellung für ausgehenden Traffic den Wert all-traffic fest. Richten Sie Cloud NAT ein, um das öffentliche Internet mit hoher Bandbreite zu erreichen.

Erwägungen zu Build, Bereitstellung, Laufzeit und Systemdesign

In den folgenden Abschnitten werden Erwägungen zu Build, Bereitstellung, Laufzeit und Systemdesign beschrieben.

Während der Erstellung

In der folgenden Liste finden Sie Erwägungen, die Sie bei der Planung Ihres Builds berücksichtigen müssen:

  • Wählen Sie ein gutes Basis-Image aus. Beginnen Sie mit einem Image aus den Deep Learning Containern oder der NVIDIA-Container Registry für das verwendete ML-Framework. Bei diesen Images sind die neuesten leistungsrelevanten Pakete bereits installiert. Wir raten davon ab, ein benutzerdefiniertes Image zu erstellen.
  • Wählen Sie 4-Bit-quantisierte Modelle aus, um die Nebenläufigkeit zu maximieren, es sei denn, Sie können nachweisen, dass sie die Ergebnisqualität beeinträchtigen. Durch die Quantisierung entstehen kleinere und schnellere Modelle, wodurch der für die Modellbereitstellung benötigte GPU-Speicher reduziert und die Parallelität zur Laufzeit erhöht werden kann. Idealerweise sollten die Modelle mit der Zielbittiefe trainiert werden, anstatt auf diese herunterquantisiert zu werden.
  • Wählen Sie ein Modellformat mit schnellen Ladezeiten aus, um die Startzeit des Containers zu minimieren, z. B. GGUF. Diese Formate spiegeln den Zielquantisierungstyp genauer wider und erfordern weniger Transformationen, wenn sie in die GPU geladen werden. Verwenden Sie aus Sicherheitsgründen keine Prüfpunkte im Pickle-Format.
  • LLM-Caches beim Build erstellen und vorwärmen Starten Sie das LLM auf der Build-Maschine, während Sie das Docker-Image erstellen. Aktivieren Sie das Prompt-Caching und verwenden Sie gängige oder Beispiel-Prompts, um den Cache für die reale Nutzung vorzuwärmen. Speichern Sie die Ausgaben, die generiert werden, damit sie zur Laufzeit geladen werden können.
  • Speichern Sie Ihr eigenes Inferenzmodell, das Sie während der Erstellung generieren. Dadurch sparen Sie viel Zeit im Vergleich zum Laden weniger effizient gespeicherter Modelle und zum Anwenden von Transformationen wie der Quantisierung beim Starten des Containers.

Bei der Bereitstellung

In der folgenden Liste finden Sie Erwägungen, die Sie bei der Planung Ihrer Bereitstellung berücksichtigen müssen:

  1. Achten Sie darauf, dass Sie die Dienstnebenläufigkeit in Cloud Run genau festlegen.
  2. Passen Sie die Startprüfungen an Ihre Konfiguration an.

Startprüfungen ermitteln, ob der Container gestartet wurde und bereit ist, Traffic entgegenzunehmen. Beachten Sie beim Konfigurieren von Startprüfungen die folgenden wichtigen Punkte:

  • Angemessene Startzeit: Geben Sie dem Container, einschließlich der Modelle, genügend Zeit, um vollständig initialisiert und geladen zu werden.
  • Überprüfung der Modellbereitschaft: Konfigurieren Sie die Prüfung so, dass sie nur erfolgreich ist, wenn Ihre Anwendung bereit ist, Anfragen zu verarbeiten. Viele Serving-Engines erreichen dies automatisch, wenn das Modell in den GPU-Arbeitsspeicher geladen wird, wodurch vorzeitige Anfragen verhindert werden.

Beachten Sie, dass Ollama einen TCP-Port öffnen kann, bevor ein Modell geladen wird. Um dies zu beheben können Sie

  • Modelle vorab laden: In der Ollama-Dokumentation finden Sie eine Anleitung zum Vorabladen Ihres Modells während des Starts.

Während der Laufzeit

  • Verwalten Sie aktiv die unterstützte Kontextlänge. Je kleiner das unterstützte Kontextfenster, desto mehr Anfragen können parallel ausgeführt werden. Die Details dazu hängen vom Framework ab.
  • Verwenden Sie die LLM-Caches, die Sie während der Erstellung generiert haben. Geben Sie dieselben Flags an, die Sie während der Erstellung verwendet haben, als Sie den Prompt- und Präfix-Cache generiert haben.
  • Laden Sie das gespeicherte Modell, das Sie gerade geschrieben haben. Unter Vor- und Nachteile beim Speichern und Laden von Modellen finden Sie einen Vergleich der Möglichkeiten zum Laden des Modells.
  • Verwenden Sie einen quantisierten Schlüssel/Wert-Paar-Cache, wenn Ihr Framework dies unterstützt. Dadurch kann der Arbeitsspeicherbedarf pro Anfrage reduziert und eine höhere Parallelität konfiguriert werden. Dies kann sich jedoch auch auf die Qualität auswirken.
  • Legen Sie die GPU-Arbeitsspeichermenge fest, die für die Modellgewichtungen, die Aktivierung und den Schlüssel/Wert-Paar-Cache reserviert werden soll. Legen Sie den Wert so hoch wie möglich fest, ohne einen Fehler wegen zu wenig Arbeitsspeicher zu erhalten.
  • Prüfen Sie, ob Ihr Framework Optionen zur Verbesserung der Startleistung des Containers bietet (z. B. durch Parallelisierung des Ladens von Modellen).
  • Konfigurieren Sie die Nebenläufigkeit im Dienstcode richtig. Achten Sie darauf, dass der Dienstcode so konfiguriert ist, dass er mit den Nebenläufigkeitseinstellungen Ihres Cloud Run-Dienstes funktioniert.

Auf Systemdesignebene

  • Fügen Sie gegebenenfalls semantische Caches hinzu. In einigen Fällen kann das Caching ganzer Anfragen und Antworten eine gute Möglichkeit sein, die Kosten für häufige Anfragen zu begrenzen.
  • Kontrollieren Sie die Varianz in Ihren Präambeln. Prompt-Caches sind nur nützlich, wenn sie die Prompts in der richtigen Reihenfolge enthalten. Caches werden effektiv als Präfix-Cache verwendet. Einfügungen oder Änderungen in der Sequenz führen dazu, dass sie entweder nicht im Cache gespeichert oder nur teilweise vorhanden sind.

Autoscaling und GPUs

Wenn Sie das standardmäßige Autoscaling von Cloud Run verwenden, skaliert Cloud Run die Anzahl der Instanzen jeder Version automatisch basierend auf Faktoren wie CPU Auslastung und Anfrageparallelität. Cloud Run skaliert die Anzahl der Instanzen jedoch nicht automatisch basierend auf der GPU-Auslastung.

Wenn eine Version mit einer GPU keine erhebliche CPU-Auslastung aufweist, wird Cloud Run für die Anfrageparallelität hochskaliert. Um eine optimale Skalierung für die Anfrageparallelität zu erreichen, müssen Sie eine optimale maximale Anzahl gleichzeitiger Anfragen pro Instanz festlegen, wie im nächsten Abschnitt beschrieben.

Maximale Anzahl gleichzeitiger Anfragen pro Instanz

Mit der Einstellung für die maximale Anzahl gleichzeitiger Anfragen pro Instanz wird die maximale Anzahl von Anfragen gesteuert, die Cloud Run gleichzeitig an eine einzelne Instanz sendet. Sie müssen die Nebenläufigkeit anpassen, damit sie der maximalen Parallelität entspricht, die der Code in jeder Instanz mit guter Leistung verarbeiten kann.

Maximale Nebenläufigkeit und KI-Arbeitslasten

Wenn Sie eine KI-Inferenzarbeitslast auf einer GPU in jeder Instanz ausführen, hängt die maximale Nebenläufigkeit, die der Code mit guter Leistung verarbeiten kann, von den spezifischen Details des Frameworks und der Implementierung ab. Die folgenden Faktoren wirken sich darauf aus, wie Sie die optimale Einstellung für die maximale Anzahl gleichzeitiger Anfragen festlegen:

  • Anzahl der Modellinstanzen, die in die GPU geladen werden
  • Anzahl paralleler Anfragen pro Modell
  • Verwendung von Batching
  • Spezifische Konfigurationsparameter für Batching
  • Anteil der Arbeit, die nicht auf der GPU ausgeführt wird

Wenn die maximale Anzahl gleichzeitiger Anfragen zu hoch eingestellt ist, kann es passieren, dass Anfragen innerhalb der Instanz auf den Zugriff auf die GPU warten müssen, was zu einer erhöhten Latenz führt. Wenn die maximale Anzahl gleichzeitiger Anfragen zu niedrig eingestellt ist, kann es zu einer Unterauslastung der GPU kommen, was dazu führt, dass Cloud Run mehr Instanzen horizontal skaliert als nötig.

Eine Faustregel für die Konfiguration der maximalen Anzahl gleichzeitiger Anfragen für KI-Arbeitslasten lautet:

(Number of model instances * parallel queries per model) + (number of model instances * ideal batch size)

Angenommen, eine Instanz lädt 3 Modellinstanzen in die GPU und jede Modellinstanz kann 4 parallele Anfragen verarbeiten. Die ideale Batch-Größe ist ebenfalls 4, da dies die Anzahl der parallelen Abfragen ist, die jede Modellinstanz verarbeiten kann. Nach der Faustregel würden Sie die maximale Anzahl gleichzeitiger Anfragen auf 24 festlegen: (3 * 4) + (3 * 4).

Beachten Sie, dass diese Formel nur eine Faustregel ist. Die optimale Einstellung für die maximale Anzahl gleichzeitiger Anfragen hängt von den spezifischen Details Ihrer Implementierung ab. Um die tatsächlich optimale Leistung zu erzielen, empfehlen wir Ihnen, Ihren Dienst mit verschiedenen Einstellungen für die maximale Anzahl gleichzeitiger Anfragen einen Lasttest durchzuführen, um zu ermitteln, welche Option die beste Leistung erzielt.

Kompromisse zwischen Durchsatz, Latenz und Kosten

Unter Kompromisse zwischen Durchsatz, Latenz und Kosten finden Sie Informationen zu den Auswirkungen der maximalen Anzahl gleichzeitiger Anfragen auf Durchsatz, Latenz und Kosten. Beachten Sie, dass für alle Cloud Run-Dienste, die GPUs verwenden, die instanzbasierte Abrechnung konfiguriert sein muss.