Comment les optimisations Full-Stack NIM permettent d'obtenir 2,5 fois plus d'utilisateurs sur Nemotron 3 Ultra

Les optimisations NIM full-stack de NVIDIA sur Nemotron 3 Ultra augmentent suffisamment le débit de service pour atteindre 2,5 fois plus d’utilisateurs simultanés par déploiement. Cette analyse détaille l’origine des gains — optimisation au niveau des kernels, du runtime et du traitement par lots — et ce que les équipes doivent vérifier avant de supposer des résultats similaires sur leurs propres charges de travail.

Lecture audio non disponible dans ce navigateur
Comment les optimisations Full-Stack NIM permettent d'obtenir 2,5 fois plus d'utilisateurs sur Nemotron 3 Ultra

Tags

Résumé rapide

Les optimisations NIM full-stack de NVIDIA sur Nemotron 3 Ultra augmentent suffisamment le débit de service pour atteindre 2,5 fois plus d’utilisateurs simultanés par déploiement. Cette analyse détaille l’origine des gains — optimisation au niveau des kernels, du runtime et du traitement par lots — et ce que les équipes doivent vérifier avant de supposer des résultats similaires sur leurs propres charges de travail.

Comment les optimisations NIM full-stack permettent de servir 2,5 fois plus d’utilisateurs sur Nemotron 3 Ultra

Le blog IA de NVIDIA rapporte que les optimisations NIM full-stack permettent de servir 2,5 fois plus d’utilisateurs sur Nemotron 3 Ultra. Cette seule phrase contient trois affirmations faciles à confondre : une portée d’optimisation (« full-stack »), un résultat de capacité (« 2,5 fois plus d’utilisateurs ») et une cible précise (Nemotron 3 Ultra servi via NIM). La valeur du résultat dépend entièrement du maintien de ces trois éléments séparés.

Cet article détaille ce qu’implique une affirmation d’optimisation « full-stack » pour un déploiement de service, parcourt les couches pratiques que vous configurez réellement, et fournit des étapes d’installation, de configuration et de mesure que vous pouvez exécuter sur votre propre matériel. Le chiffre phare est rapporté par le fournisseur à partir d’une source primaire unique ; l’objectif ici est de vous aider à comprendre le mécanisme suffisamment bien pour le tester face à votre propre charge de travail.

Ce que mesure réellement le chiffre de 2,5x

L’expression « plus d’utilisateurs » est une métrique de capacité, pas une métrique de qualité. Rien dans l’affirmation ne suggère que Nemotron 3 Ultra produit de meilleures réponses après optimisation. L’affirmation est que la même empreinte matérielle peut servir environ 2,5 fois plus d’utilisateurs simultanés — vraisemblablement tout en maintenant la latence et le débit dans des limites de niveau de service acceptables.

Cette distinction compte en pratique. Un système de service a au moins trois variables concurrentes :

  • Concurrence : combien de requêtes sont en cours simultanément.
  • Latence : combien de temps prend chaque requête, généralement mesurée à p50 et p95/p99.
  • Coût par token : combien de temps GPU chaque token généré consomme.

Vous pouvez toujours augmenter la concurrence en dégradant la latence. Vous pouvez toujours réduire la latence en refusant la concurrence. Une affirmation de capacité de 2,5x n’a de sens que lorsqu’elle nomme la contrainte restée fixe. Dans un billet de blog de fournisseur, la contrainte fixe la plus probable est une cible de latence ou une configuration matérielle ; si votre propre SLO diffère, votre multiplicateur différera aussi.

Interprétation, pas fait vérifié : le chiffre de 2,5x doit être traité comme un résultat rapporté par le fournisseur, mesuré dans les conditions décrites dans le billet original. Le généraliser à votre cluster exige de reproduire ces conditions, ce qui explique pourquoi la section sur la mesure ci-dessous est aussi importante que celle sur l’installation.

Pourquoi « full-stack » est le mot qui compte

Les optimisations de service se cumulent rarement comme on l’attend. Un noyau d’attention plus rapide peut réduire de 15 % le temps de décodage, mais si l’ordonnanceur est inactif en attendant un cache KV saturé, le débit de bout en bout bouge à peine. Les gains qui produisent un multiplicateur phare proviennent généralement de l’élimination de plusieurs goulots d’étranglement séquentiels afin qu’aucune couche unique ne devienne le plafond.

Le cadrage « full-stack » dans le billet de NVIDIA renvoie à cet effet de cumul : des changements au niveau du modèle, de l’exécution, du service et de l’infrastructure appliqués ensemble plutôt qu’isolément. Appliqués séparément, chaque changement peut sembler anodin. Appliqués ensemble, ils peuvent déplacer le point de fonctionnement de tout le système.

Un bon modèle mental est une chaîne de tuyaux. Le débit de la chaîne est fixé par le tuyau le plus étroit. L’optimisation full-stack signifie élargir chaque tuyau à peu près proportionnellement, afin qu’aucun ne domine.

Les couches d’une pile de service NIM

NIM (NVIDIA Inference Microservices) empaquette un modèle avec un runtime et une interface HTTP dans un conteneur. Quand on parle d’optimiser « la pile », on désigne généralement un sous-ensemble de ces couches :

1. Couche modèle et point de contrôle. Précision des poids, format de quantification et noyaux spécifiques à l’architecture. Les changements ici modifient à la fois l’empreinte mémoire et le débit arithmétique.

2. Couche runtime et noyaux. Le moteur d’inférence, les noyaux fusionnés, les implémentations d’attention et les allocateurs de mémoire. C’est là que la latence par token est généralement gagnée ou perdue.

3. Couche service. Traitement par lots continu, ordonnancement des requêtes, gestion et pagination du cache KV, mise en cache des préfixes et contrôle d’admission. C’est là que la concurrence est gagnée ou perdue.

4. Couche infrastructure. Topologie GPU, parallélisme tensoriel entre appareils, bande passante d’interconnexion, chemins de transfert CPU–GPU et dimensionnement de la mémoire hôte.

5. Couche client et application. Délais d’attente, comportement de nouvelle tentative, mise en commun des connexions, streaming et tailles de charge utile. Un client qui ouvre une nouvelle connexion TLS par requête peut annuler les gains côté serveur.

Le résultat de 2,5x se situe à l’intersection des couches 2 à 4. La couche 5 est celle que les équipes ignorent le plus souvent, avant de ne pas réussir à reproduire les chiffres du fournisseur.

Prérequis

Avant d’exécuter quoi que ce soit, confirmez que vous disposez d’une base de référence fonctionnelle. Vous aurez besoin de :

  • Matériel GPU NVIDIA avec suffisamment de mémoire agrégée pour contenir le modèle plus une marge pour le cache KV.
  • Un pilote NVIDIA compatible installé sur l’hôte.
  • Docker (ou un runtime de conteneur compatible) installé et en cours d’exécution.
  • Le NVIDIA Container Toolkit, pour que les conteneurs puissent accéder aux GPU.
  • Une clé API NGC, si le conteneur de modèle est tiré du registre NVIDIA.
  • Python 3.9+ avec requests (ou le client compatible OpenAI) pour les tests.
  • Un SLO de latence défini — sans lui, « 2,5 fois plus d’utilisateurs » est infalsifiable.

Notez que le chemin du registre de conteneurs, le tag du modèle et les paramètres de réglage disponibles sont spécifiques au modèle. Les commandes ci-dessous utilisent des espaces réservés là où une valeur spécifique au modèle est attendue, et vous devez les renseigner à partir de la documentation du conteneur lui-même, et non de cet article.

Installation étape par étape

1. Vérifier le GPU et le pilote

Commencez par confirmer que l’hôte voit les GPU et rapporte une version de pilote.

nvidia-smi

Si cela échoue, résolvez l’installation du pilote avant de continuer. Rien en aval ne fonctionnera.

2. Vérifier Docker

Vérifiez que Docker est installé et que le démon est joignable.

docker --version && docker info | head -n 20

3. Installer le NVIDIA Container Toolkit

Installez le toolkit qui permet à Docker d’exposer les GPU aux conteneurs. Sur Debian/Ubuntu, le nom du paquet est nvidia-container-toolkit ; suivez la documentation du toolkit de NVIDIA pour la configuration exacte du dépôt correspondant à votre distribution.

sudo apt-get update
sudo apt-get install -y nvidia-container-toolkit

4. Configurer Docker pour utiliser le runtime NVIDIA

Pointez le démon Docker vers le runtime NVIDIA, puis redémarrez-le.

sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker

5. Confirmer l’accès GPU depuis un conteneur

Exécutez un conteneur minimal et vérifiez que nvidia-smi fonctionne dans le contexte du conteneur. Choisissez un tag de base CUDA compatible avec votre pilote.

docker run --rm --gpus all nvidia/cuda:12.4.0-base-ubuntu22.04 nvidia-smi

<!-- -->

export NGC_API_KEY="<your-ngc-api-key>"

6. S’authentifier auprès du registre de conteneurs

Connectez-vous au registre NVIDIA à l’aide de votre clé API. Le nom d’utilisateur littéral $oauthtoken est la convention documentée.

echo "$NGC_API_KEY" | docker login nvcr.io --username '$oauthtoken' --password-stdin

7. Préparer un cache de modèle persistant

Les conteneurs NIM mettent généralement en cache les poids téléchargés sur un volume monté, afin que les redémarrages ne les retéléchargent pas. Créez le répertoire et exportez son chemin.

export LOCAL_NIM_CACHE="$HOME/.cache/nim"
mkdir -p "$LOCAL_NIM_CACHE"

8. Lancer le conteneur NIM

Démarrez le conteneur avec un cache monté, la clé API transmise depuis l’environnement, un port publié et une mémoire partagée suffisamment dimensionnée pour le runtime. Remplacez la référence d’image par celle de votre modèle.

docker run --rm --runtime=nvidia --gpus all \
  --shm-size=16g \
  -e NGC_API_KEY \
  -v "$LOCAL_NIM_CACHE:/opt/nim/.cache" \
  -p 8000:8000 \
  nvcr.io/nim/<org>/<model>:<tag>

Le premier lancement téléchargera les poids et peut prendre plusieurs minutes. Les lancements suivants réutilisent le cache.

Configuration pour une concurrence plus élevée

La plupart des réglages liés à la concurrence se trouvent dans les couches runtime et service. Le conteneur les expose généralement sous forme de variables d’environnement ou d’un paramètre de profil. Les noms exacts sont documentés par modèle, mais les catégories sont cohérentes :

  • Longueur de séquence maximale. Plafonner la longueur de contexte réduit l’empreinte du cache KV par requête, ce qui augmente directement le nombre de requêtes tenant en mémoire. C’est souvent le réglage le plus déterminant pour la concurrence.
  • Budget mémoire du cache KV. Réserver explicitement une fraction de la mémoire GPU au cache KV empêche l’allocateur de se fragmenter ou de déborder sous charge.
  • Limites de taille de lot. Le traitement par lots continu permet à de nouvelles requêtes de rejoindre un lot en cours. Augmenter le plafond aide le débit mais peut nuire à la latence de queue ; le baisser fait l’inverse.
  • Degré de parallélisme tensoriel. Pour les modèles qui ne tiennent pas sur un seul appareil, la répartition sur plusieurs GPU modifie à la fois la marge mémoire et la sensibilité à l’interconnexion.
  • Sélection des GPU. Épingler à des appareils spécifiques évite les effets de voisin bruyant sur des hôtes partagés.

Un schéma pratique consiste à passer ces réglages sous forme de variables d’environnement au lancement et à les conserver dans un fichier versionné plutôt que dans l’historique de votre shell :

docker run --rm --runtime=nvidia --gpus '"device=0,1"' \
  --shm-size=32g \
  -e NGC_API_KEY \
  -e MAX_SEQUENCE_LENGTH=8192 \
  -e KV_CACHE_FRACTION=0.85 \
  -v "$LOCAL_NIM_CACHE:/opt/nim/.cache" \
  -p 8000:8000 \
  nvcr.io/nim/<org>/<model>:<tag>

Modifiez une variable à la fois et enregistrez la distribution de latence à chaque étape. Le réglage de la concurrence sans mesure est de la spéculation.

Exemples d’utilisation

Vérifier que le service est opérationnel

Listez les modèles servis pour confirmer que le conteneur est sain et que le modèle est chargé.

curl -s http://localhost:8000/v1/models | python -m json.tool

Envoyer une requête de complétion unique

Envoyez une complétion de chat compatible OpenAI et inspectez la réponse JSON brute.

curl -s http://localhost:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
        "model": "nemotron",
        "messages": [{"role": "user", "content": "Explain prefix caching in one paragraph."}],
        "max_tokens": 128
      }' | python -m json.tool

La valeur model doit correspondre à l’identifiant renvoyé par le point de terminaison /v1/models.

Appeler le point de terminaison depuis Python

Pour l’intégration applicative, utilisez un client avec réutilisation de connexion plutôt que de créer une nouvelle session à chaque appel.

import requests

session = requests.Session()
response = session.post(
    "http://localhost:8000/v1/chat/completions",
    json={
        "model": "nemotron",
        "messages": [{"role": "user", "content": "Summarize KV cache paging."}],
        "max_tokens": 128,
    },
    timeout=120,
)
response.raise_for_status()
print(response.json()["choices"][0]["message"]["content"])

Mesurer la courbe de concurrence

Le test le plus utile est un balayage : gardez la charge utile de requête constante et augmentez la concurrence, en enregistrant le taux de réussite et les centiles de latence à chaque étape. Le point où la latence p95 franchit votre SLO est votre capacité effective.

import concurrent.futures
import time
import requests

URL = "http://localhost:8000/v1/chat/completions"
PAYLOAD = {
    "model": "nemotron",
    "messages": [{"role": "user", "content": "Write two sentences about batching."}],
    "max_tokens": 128,
}

def one_call(_):
    start = time.perf_counter()
    try:
        r = requests.post(URL, json=PAYLOAD, timeout=180)
        return r.status_code, time.perf_counter() - start
    except requests.RequestException:
        return 0, time.perf_counter() - start

def sweep(concurrency, total=64):
    with concurrent.futures.ThreadPoolExecutor(max_workers=concurrency) as pool:
        results = list(pool.map(one_call, range(total)))
    latencies = sorted(d for s, d in results if s == 200)
    if not latencies:
        print(f"concurrency={concurrency}: all requests failed")
        return
    p50 = latencies[len(latencies) // 2]
    p95 = latencies[int(len(latencies) * 0.95) - 1]
    print(f"concurrency={concurrency:>3} ok={len(latencies):>3}/{total} "
          f"p50={p50:.2f}s p95={p95:.2f}s")

for c in (1, 2, 4, 8, 16, 32):
    sweep(c)

Exécutez ceci d’abord contre une configuration non optimisée, puis contre une configuration réglée. Le rapport entre les deux niveaux de concurrence à votre plafond de latence est votre propre multiplicateur — qui peut ou non ressembler à 2,5x.

Comment vérifier le gain sur votre propre charge de travail

Les benchmarks de fournisseurs sont généralement exécutés sur du matériel contrôlé avec des distributions de requêtes contrôlées. Votre trafic ne correspondra probablement pas. Pour obtenir un chiffre défendable :

  1. Figez la distribution des requêtes. Échantillonnez de vrais prompts issus de la production et rejouez-les, plutôt que d’utiliser des prompts synthétiques d’une seule phrase.
  2. Fixez le plafond de latence. Définissez le p95 que vous pouvez tolérer avant de commencer le test.
  3. Modifiez une couche à la fois. Établissez la base de référence, puis appliquez les changements de modèle, de runtime, de service et d’infrastructure séquentiellement, en enregistrant la capacité après chacun.
  4. Surveillez le déplacement du plafond. Si la capacité cesse de s’améliorer, vous avez atteint un nouveau goulot d’étranglement — souvent le CPU hôte, le réseau ou les limites de connexion côté client, plutôt que le GPU.
  5. Répétez les exécutions. Les benchmarks de service sont sensibles à l’échauffement, à l’état du cache et au comportement des horloges.

Maintenir constants la distribution des requêtes et le plafond de latence est ce qui transforme un chiffre marketing en résultat d’ingénierie.

Limites et questions ouvertes

Quelques mises en garde honnêtes sur ce sujet :

  • Preuve à source unique. Le chiffre de 2,5x provient d’un seul billet de blog de fournisseur. Il n’a pas été répliqué indépendamment ici, et les conditions de benchmark sous-jacentes ne sont pas répétées dans cet article.
  • Spécificité matérielle. Les multiplicateurs de ce type sont liés à une configuration GPU, une taille de modèle et un mélange de requêtes spécifiques. Appliqué à un déploiement différent, le chiffre risque de bouger dans un sens ou dans l’autre.
  • Définition des « utilisateurs ». Le terme n’est pas normalisé. Il peut signifier sessions simultanées, requêtes par seconde ou clients distincts dans une fenêtre. Chacun implique une mesure différente.
  • Durabilité des optimisations. Le réglage full-stack dépend de la configuration. Une mise à jour de conteneur, un changement de pilote ou une évolution de la forme du trafic peuvent invalider des paramètres soigneusement réglés.
  • Pas de repas gratuit en matière de latence. Augmenter la capacité signifie généralement accepter une latence par requête quelque peu plus élevée. Le compromis doit être choisi délibérément, pas découvert en production.

Aucune de ces mises en garde ne rend le résultat inintéressant. Elles définissent simplement la frontière dans laquelle l’affirmation est utilisable.

Conclusion

Le chiffre phare de 2,5x du billet de NVIDIA se lit mieux comme un résultat système que comme un résultat de modèle. Nemotron 3 Ultra est la charge de travail cible ; NIM est le mécanisme de livraison ; « full-stack » décrit la portée du réglage ; « plus d’utilisateurs » décrit la capacité sous une contrainte que le billet original définit.

Pour les ingénieurs, l’enseignement pratique est structurel. La capacité de service est limitée par la couche la plus étroite de la chaîne, de sorte que les micro-optimisations isolées produisent rarement des sauts. Les améliorations de quantification, de noyaux, de traitement par lots, de gestion du cache KV et de topologie GPU doivent être appliquées de concert — puis mesurées par rapport à un plafond de latence fixe et à une distribution de requêtes réaliste.

Installez le toolkit, montez le conteneur, balayez la concurrence et trouvez votre propre plafond. Le multiplicateur que vous mesurez sur votre matériel avec votre trafic est le seul qui compte pour votre planification de capacité.

Sources