Compétence DeepStream Profiling Skill
Création de pipeline pilotée par le profiling. Quand l'utilisateur indique qu'il veut un pipeline DeepStream efficace, cette compétence remplace la conjecture par deux chiffres mesurés — batch d'inférence plateau et plafond matériel — et en dérive chaque autre configuration. Puis elle profile le pipeline E2E avec Nsight Systems et rapporte les timings NVTX par plugin.
Agnostique au modèle et au pipeline. La compétence suppose uniquement que l'élément d'inférence est nvinfer ou nvinferserver (les dims du modèle, la précision et les curseurs de batch sont donc réglables via la config standard). Elle fonctionne pour les pipelines de détection (avec ou sans tracker), classification, segmentation, VLM et embedding. La source peut être fichier, RTSP, caméra USB ou n'importe quel mélange. La compétence lit la config actuelle de l'utilisateur pour découvrir les dims du modèle / FPS cible / propriétés de la source — elle n'assume AUCUN modèle, codec ou résolution particulier.
Contrainte. Terminal uniquement. Utilisez
nsys profilepour capturer etnsys statspour extraire. Ne dépendez pas de Nsight Lens ou d'une quelconque interface graphique.
Quand déclencher
Activez cette compétence au moment de la création du pipeline quand la demande de l'utilisateur porte une intention d'efficacité. Déclencheurs concrets :
- "construire un pipeline efficace / rapide / performant / optimisé"
- "donne-moi un pipeline qui tourne bien sur ce GPU"
- "benchmark / profile / mesure / tuner / optimiser ce pipeline"
- "je veux exécuter N flux à M FPS"
- "combien de flux ce GPU peut-il gérer"
- l'utilisateur demande explicitement
nsysou Nsight
Pour un simple "construire un pipeline" / "afficher cette vidéo" / "enregistrer ce flux" sans intention de performance, passez plutôt à la compétence deepstream-generate-pipeline.
Le flux à 6 étapes
Exécutez les étapes dans l'ordre. L'étape 0 s'exécute avant la génération du pipeline, donc l'utilisateur part d'un squelette accordé aux perfs. Les étapes 1–5 mesurent et vérifient.
Étape 0 — Application de présélection (au moment de la création du pipeline)
Déclencheur : chaque fois que l'agent de codage est sur le point de générer un nouveau pipeline DS ET que le prompt de l'utilisateur porte une intention d'efficacité (voir "Quand déclencher" ci-dessus).
Action : appliquez ces valeurs par défaut sans demander. L'utilisateur n'a pas besoin de les connaître ; il obtient juste un pipeline déjà dans la bonne forme.
| Curseur | Valeur par défaut | Ignorer quand |
|---|---|---|
nvinfer.network-mode |
1 (INT8) si un fichier de calibrage est présent à int8-calib-file=<path>, sinon 2 (FP16). Jamais FP32. |
Le modèle n'a pas de calibrage INT8 ET l'utilisateur dit explicitement "FP32". |
nvinfer.model-engine-file |
Chemin .engine pré-construit |
Toujours défini. Forcer une précompilation ponctuelle avant la mesure. |
nvinfer.infer-dims |
3;<H>;<W> correspondant à l'entrée native du modèle |
Toujours défini, même pour ONNX de forme statique (inoffensif). |
nvstreammux.batch-size |
min(N_streams, 16) jusqu'à ce que microbench l'affine |
— |
nvstreammux.width / height |
dims d'entrée native du modèle (lues à partir de infer-dims=3;H;W dans la config nvinfer) |
L'utilisateur demande explicitement la résolution native de la source au muxer. |
nvstreammux.batched-push-timeout |
1e6 / source_fps µs (33333 pour 30 fps) |
— |
nvstreammux.nvbuf-memory-type |
0 (NVMM) |
— |
Décodeur num-extra-surfaces |
min(batch_size, 5) |
— |
Décodeur cudadec-memtype |
0 (NVMM) |
— |
| Sink | fakesink sync=False pour la variante benchmark |
L'utilisateur a demandé un affichage à l'écran ou un enregistrement sur disque (garder alors OSD/tiler/encoder/sink et produire DEUX variantes). |
| OSD + tiler | omettre | L'utilisateur a demandé une sortie visible. |
Tracker ll-config-file |
config_tracker_NvDCF_max_perf.yml (présélection NvDCF accordée aux perfs expédiée avec DS 9.0) |
Tracker absent. |
Tracker tracker-width / height |
480 / 288 | — |
Tracker enable-batch-process (dans YAML lié) |
1 |
— |
| Queue entre source et pgie | max-size-buffers = batch_size × 4 |
Aucune queue demandée (rare). |
| Kafka/message queue | max-size-buffers=2, leaky=2 |
Pas de Kafka. |
PerfMonitor côté décodage |
attacher (en plus du côté pgie) | Pipeline est nvurisrcbin → pgie direct sans queue intermédiaire. |
Pourquoi l'étape 0 existe : sans elle, chaque nouveau pipeline généré démarre avec des valeurs par défaut axées sur l'affichage et les étapes 1–5 passent du temps à corriger des problèmes évitables. L'étape 0 est la porte "ne écrivez pas un mauvais pipeline en premier lieu".
L'utilisateur étudiant / API ne voit jamais ces curseurs. La réponse de la compétence à l'utilisateur est en anglais simple (FPS, nombre de flux, goulot d'étranglement observé), non des noms de curseurs.
Le flux de vérification (étapes 1–5)
Exécutez les étapes dans l'ordre. Ne sautez pas une étape — les étapes ultérieures dépendent des résultats des étapes antérieures.
Étape 1 — Vérification de la couverture NVTX
Les plugins DeepStream émettent nativement des plages NVTX ; les plugins personnalisés et les éléments GStreamer-core simples (queue, tee, h264parse, etc.) ne le font pas. Avant le profiling, listez les éléments que le pipeline utilise et classifiez chacun.
- Lisez la définition du pipeline (chaîne gst-launch ou
pipeline.py). - Pour chaque élément, cherchez-le dans references/nvtx-coverage.md.
- Classifiez COVERED (émet NVTX dans ce DS / image / combo nsys) ou UNINSTRUMENTED.
- Règle MVP : la compétence préfère les NVTX par plugin comme confirmation mais ne l'exige pas.
Le diagnostic lié au décodage fonctionne à partir de la forme microbench +
nvidia-smi dmon; lié au calcul à partir du mix de kernel CUDA ; memcpy à partir decuda_gpu_mem_time_sum. NVTX est un bonus. - Pour les éléments UNINSTRUMENTED, la compétence rapporte "non directement mesurable dans cette version" et applique quand même les curseurs R1–R6 en forme fermée (qui dérivent des entrées, non des données de profil par plugin).
- L'injection auto-NVTX pour les éléments non instrumentés est hors périmètre pour cette version — signalez-le comme suivi dans le rapport final.
Résultat de l'étape 1 : un tableau court de couverture, p. ex.
nvurisrcbin COVERED
nvstreammux COVERED
nvinfer COVERED
nvtracker COVERED
queue_src UNINSTRUMENTED — pas ré-accordé
fakesink UNINSTRUMENTED — pas ré-accordé
Étape 2 — Découverte matérielle
Exécutez nvidia-smi et dérivez les plafonds théoriques du GPU hôte. Requêtes minimales :
# Identité + mémoire + calcul
nvidia-smi --query-gpu=name,compute_cap,memory.total,memory.free,\
clocks.max.sm,clocks.max.memory,utilization.gpu \
--format=csv,noheader,nounits
# Utilisation NVDEC / NVENC (par moteur)
nvidia-smi --query-gpu=utilization.decoder,utilization.encoder \
--format=csv,noheader,nounits
# Largeur/gen du lien PCIe (pour le plafond memcpy H2D)
nvidia-smi --query-gpu=pcie.link.gen.current,pcie.link.width.current \
--format=csv,noheader,nounits
Dérivez de ces chiffres :
- Plafond de décodage (fps) : NVDEC_count × fps par unité H265/H264 pour la résolution source (tableau dans references/hw-ceiling-formulas.md).
- Plafond de calcul (TOPS) : nombre SM × horloge × ops-par-horloge à la précision cible. Donne une borne supérieure — les vrais modèles atteignent 30–60 % de ceci.
- Plafond de bande passante mémoire (GB/s) : horloge mémoire × largeur bus. Les lectures de poids du modèle + activations doivent bien tenir sous ceci.
- Plafond memcpy (GB/s) : gen PCIe × largeur × 0,8 pratique. Pertinent uniquement si NVMM est cassé et les transferts H2D/D2H apparaissent dans l'étape 5.
Stockez les plafonds dérivés — ils pilotent la section "réel vs. théorique" de l'étape 5.
Formules complètes et tableau de débit NVDEC par codec : references/hw-ceiling-formulas.md.
Étape 3 — Micro-benchmark inférence uniquement
Exécutez uniquement l'étape d'inférence (source → streammux → nvinfer → fakesink), en balayant
batch-size pour trouver le plateau. Ceci isole le vrai FPS de crête du modèle de tout le reste, et répond à "combien de flux rentrent dans un seul batch sans baisse FPS ?".
Balayage : batch-size ∈ {1, 2, 4, 8, 16, 32} (limité à N_streams et à la mémoire GPU).
Pour chaque taille de batch :
- Définissez
nvstreammux.batch-size = nvinfer.batch-size = B. - Définissez
nvstreammux.width/height= lesinfer-dimsnatives du modèle (lues à partir de la config nvinfer). fakesink sync=Falsecomme seule branche.- Exécutez 30 s ; mesurez FPS à partir de
measure_fps_probe(console) ou DSPerfMonitor. - Enregistrez
(B, fps).
Batch de plateau = le plus petit B où augmenter à 2×B donne < 5 % de gain FPS. C'est le batch cible pour le pipeline complet.
Si le N_streams de l'utilisateur ≤ batch de plateau, définissez batch final = N_streams. Sinon définissez batch final = batch de plateau et notez que le pipeline traitera les flux en plusieurs batchs par tick.
Étape 4 — Dériver les configs
À partir de (plateau_batch, HW_ceilings, N_streams, source_res, source_fps), définissez chaque curseur tunable à la fois. Ne tuner pas un curseur à la fois — les règles de dérivation sont en forme fermée.
Curseurs à définir, dans l'ordre :
- Streammux :
batch-size = final_batch,width/height = min(source_res, infer_dims),batched-push-timeout = 1e6 / source_fpsµs,nvbuf-memory-type = 0. - Inférence :
batch-size = final_batch,network-mode = 1 (INT8) si le fichier calib existe sinon 2 (FP16),interval = 0,infer-dims = dims natives du modèle,model-engine-file = chemin.engine` pré-construit. - Décodeur (sur
nvurisrcbin/nvmultiurisrcbin/nvv4l2decoder) :num-extra-surfaces = min(final_batch, 5),cudadec-memtype = 0,nvbuf-memory-type = 0. - Tracker (si présent) :
enable-batch-process = 1, résolution tracker 480×288, pointezll-config-filesurconfig_tracker_NvDCF_max_perf.yml. - Queues (si présentes entre décodeur et streammux, ou streammux et nvinfer) :
max-size-buffers = final_batch × 2. Branches Kafka/message :leaky=2, max-size-buffers=2.
Tableau de dérivation complet avec chaque formule et une explication d'une ligne "pourquoi" : references/config-derivation-rules.md.
Écrivez les valeurs dérivées dans les fichiers config de l'utilisateur (pgie_config.yml,
tracker_config.yml, propriétés source pipeline.py, tout .txt deepstream-app). Toujours
Lire avant Éditer. Gardez les éditions chirurgicales — ne reformatez pas les lignes non liées.
Étape 5 — Profile E2E + rapport
Exécutez le pipeline E2E sous nsys profile et extrayez les timings par plugin via nsys stats.
Capturez :
TS=$(date +%Y%m%d_%H%M%S)
OUT=/tmp/ds_profile_${TS}
nsys profile \
--trace=cuda,nvtx,osrt \
--gpu-metrics-devices=all \
--cuda-memory-usage=true \
--force-overwrite=true \
--duration=30 \
--output=${OUT} \
<votre-commande-de-lancement-de-pipeline>
Extrayez :
# Temps GPU par kernel (top 10)
nsys stats --report cuda_gpu_kern_sum --format csv ${OUT}.nsys-rep | head -20
# Temps par plage NVTX (top 10) — ceci est la décomposition DS par plugin
nsys stats --report nvtx_sum --format csv ${OUT}.nsys-rep | head -20
# Totaux memcpy
nsys stats --report cuda_gpu_mem_time_sum --format csv ${OUT}.nsys-rep
# Métriques GPU (activité SM, débit DRAM) — nécessite --gpu-metrics-devices
nsys stats --report gpu_metric_gpu_util_sum --format csv ${OUT}.nsys-rep
Référence de commande complète : references/nsys-cli-recipes.md.
Rapport (Markdown, sur stdout — pas d'interface utilisateur externe) :
## Résumé du profile
**Matériel** : <name>, <mem_total> GB, SM x<sm>, NVDEC x<nvdec>, PCIe Gen<g> x<w>
**Plafonds** : décodage <X> fps, calcul ~<Y> TOPS @ INT8, mémoire <Z> GB/s
**Plateau d'inférence** : batch=<B>, pic=<F> fps par batch → <F × B> fps agrégés
**E2E mesuré** : <actual> fps (=<pct>% du plateau d'inférence)
### Temps par plugin (à partir de NVTX) — uniquement pour les plugins émettant NVTX dans cette version
| Plugin | Part du temps mural | GPU / CPU | Notes |
|-----------------|--------------------|-----------|-------|
| nvinfer | <pct>% | GPU | (toujours émis ; si absent, l'injection NVTX est cassée) |
| nvdsosd | <pct>% | GPU | (quand dans le pipeline) |
| ... | ... | ... | (autres plugins comme la sonde de vérification le montre) |
(Les nombres ci-dessus sont illustratifs — remplissez à partir de `nsys stats --report nvtx_sum`. Les plugins
qui n'émettent pas NVTX dans votre combo DS / image n'apparaissent simplement pas ; ce n'est pas un bug, c'est
la limite de ce que NVTX capture ici. Voir `references/nvtx-coverage.md`.)
### Configs appliquées (forme exemple ; les valeurs proviennent de R1–R6 + mesures de l'étape 3)
- `nvstreammux.batch-size = <plateau_batch>`
- `nvinfer.network-mode = 1 (INT8)` si calibrage disponible, sinon `2 (FP16)`
- décodeur `num-extra-surfaces = min(plateau_batch, 5)`
- queue entre source et pgie, `max-size-buffers = plateau_batch × 4`
- ... (liste complète selon la forme du pipeline de l'utilisateur)
### Non instrumenté (tune ignoré)
Listez les éléments qui n'ont pas émis NVTX dans cette version (typiquement les plugins binaires propriétaires — voir `references/nvtx-coverage.md`) plus les aides GStreamer-core simples. Rapportez-les pour que l'utilisateur sache ce qui n'était pas directement mesurable.
Gardez le résumé terse. Le CSV nsys stats brut va dans le fichier temp, pas dans la réponse.
Documents de référence
| Document | Utilisez quand |
|---|---|
| references/nvtx-coverage.md | Étape 1 — classifier chaque élément du pipeline en COVERED ou UNINSTRUMENTED. |
| references/hw-ceiling-formulas.md | Étape 2 — convertir la sortie nvidia-smi en plafonds de décodage / calcul / mémoire. |
| references/config-derivation-rules.md | Étape 4 — formule par curseur indexée sur (plateau_batch, HW, N_streams, source_res, source_fps). |
| references/nsys-cli-recipes.md | Étapes 3 & 5 — invocations exactes nsys profile / nsys stats. |
Hors périmètre (cette version)
- Pas de Nsight Lens / pas d'interface graphique. Terminal uniquement.
- Pas d'injection NVTX auto. MVP ignore leurs curseurs. Travail futur.
- Pas de boucle itérative tune-mesure-tune. L'étape 4 dérive les configs une fois à partir de règles en forme fermée ; l'étape 5 mesure et rapporte. Si l'utilisateur veut continuer à tuner, il peut ré-invoquer la compétence avec des entrées mises à jour.
Compétences connexes
deepstream-generate-pipeline— génération de pipeline en amont. Cette compétence suppose qu'un pipeline existe déjà ou est sur le point d'être généré.deepstream-byovm— construction moteur HF → TensorRT. À exécuter d'abord si l'utilisateur a apporté un nouveau modèle ; venez ici après.
Notes
- Vit dans
skills/deepstream-profile-pipeline/aux côtés des autres compétences DS, selon la convention repo dansCLAUDE.md. - Pour la vérité de base sur les propriétés de tout plugin (types, valeurs par défaut, plages) et les capacités des pads,
interrogez le binaire chargé dans le conteneur DS :
gst-inspect-1.0 nvinfer gst-inspect-1.0 nvstreammux gst-inspect-1.0 nvurisrcbin # fonctionne sur les plugins binaires propriétaires aussi gst-inspect-1.0 | grep ^nv # lister chaque élément spécifique NVIDIA que cette version expédieConvention de nommage des plugins : tout élément préfixé
nv*est spécifique NVIDIA DeepStream (capable NVMM, peut émettre NVTX) ; tout le reste est GStreamer-core en amont (pas NVMM, n'émet jamais DS NVTX). Utilisez ce préfixe comme classifieur de premier passage lors du triage d'un pipeline non familier. - Le sous-ensemble open-source du code des plugins vit sous
/opt/nvidia/deepstream/deepstream/sources/gst-plugins/si vous avez besoin de lire l'implémentation (seuls certains plugins sont open — les plugins fermés doivent être inspectés viagst-inspect-1.0et le comportement observé à l'exécution).
<!-- Marqueur de rafraîchissement de signature. -->