analyze-kernel-bottleneck
について
このスキルは、GPUカーネルを分析し、ルーフライン分析と占有率メトリクスを用いて、演算律速、メモリ律速、レイテンシ律速のいずれかに分類します。タイル化やダブルバッファリングなどの具体的な最適化戦略を推奨する意思決定マトリックスを提供します。高度なCUDAカーネルプロファイリングに活用し、パフォーマンスチューニングの取り組みを導くためにご利用ください。
クイックインストール
Claude Code
推奨npx skills add pjt222/agent-almanac -a claude-code/plugin add https://github.com/pjt222/agent-almanacgit clone https://github.com/pjt222/agent-almanac.git ~/.claude/skills/analyze-kernel-bottleneckこのコマンドをClaude Codeにコピー&ペーストしてスキルをインストールします
ドキュメント
Analyze Kernel Bottleneck
Identifica sistemáticamente si un kernel de GPU está limitado por cómputo, memoria o latencia midiendo el rendimiento de referencia, clasificándolo en el roofline, calculando la ocupación y la razón cómputo/carga por tile, inspeccionando la mezcla de instrucciones SASS y los códigos de stall, verificando el acantilado de memoria compartida, y aplicando una matriz de decisión para seleccionar la estrategia de optimización correcta.
Cuándo Usar
- Antes de optimizar cualquier kernel CUDA -- establecer la línea base y clasificar el tipo de cuello de botella
- Después de escribir una primera versión funcional de un kernel para identificar la ruta de optimización
- Cuando un kernel rinde por debajo de las expectativas relativas al pico teórico
- Al decidir entre cp.async, tiles más grandes o reestructuración algorítmica
Entradas
- Requerido: Kernel compilado (
.cubino fuente.cucon comando de build) - Requerido: Arnés de benchmark que lanza el kernel con temporización por eventos CUDA
- Requerido: Dimensiones del problema (p. ej., M, N, K para GEMM; seq_len, heads, head_dim para attention)
- Opcional: Arquitectura de GPU objetivo (predeterminado: GA104 / sm_86 / RTX 3070 Ti)
- Opcional: Porcentaje de utilización pico esperado para comparación
- Opcional: Datos de profiling previos (informes de Nsight Compute)
Procedimiento
Paso 1: Medir Rendimiento de Referencia
Ejecutar el kernel con eventos CUDA (BenchTimer), registrar tiempo en milisegundos. Calcular métricas efectivas de rendimiento:
- Compilar el kernel si aún no está construido:
nvcc --cubin -arch=sm_86 -O2 -o kernel.sm_86.cubin kernel.cu nvcc -arch=sm_86 -O2 -o bench bench.cu -lcuda -I../../phase2/common - Ejecutar con tamaños de problema representativos, asegurando que las ejecuciones de calentamiento precedan a la medición:
./bench 4096 4096 4096 - Registrar el tiempo del kernel en ms desde eventos CUDA (no reloj de pared).
- Calcular GFLOPS efectivos y ancho de banda efectivo:
- GEMM:
effective_gflops = (2 * M * N * K) / (time_ms / 1000) / 1e9 - Kernels limitados por ancho de banda:
effective_bw = total_bytes / (time_ms / 1000) / 1e9 - Flash Attention:
effective_gflops = (4 * batch * heads * seq_len^2 * head_dim) / (time_ms / 1000) / 1e9
- GEMM:
Esperado: Números de referencia: tiempo del kernel en ms, GFLOPS efectivos y ancho de banda efectivo.
En caso de fallo: Verificar que el kernel se lanza sin error (macro CHECK_CU). Verificar que las ejecuciones de calentamiento preceden a la medición. Asegurar que las dimensiones del problema son lo suficientemente grandes para saturar la GPU (problemas pequeños pueden tener cuello de botella en la sobrecarga de lanzamiento).
Paso 2: Clasificar en el Roofline
Calcular la intensidad aritmética y compararla con el punto de equilibrio de la máquina para clasificar el kernel:
- Calcular intensidad aritmética:
AI = FLOPs / bytes_loaded_from_global_memory. Contar solo bytes únicos cargados desde DRAM (no memoria compartida ni reuso de registros). - Buscar el punto de equilibrio de la máquina:
balance = peak_compute / peak_bandwidth. - Clasificar: Si
AI < balance, el kernel está limitado por memoria. SiAI > balance, el kernel está limitado por cómputo.
Valores de Referencia GA104 (RTX 3070 Ti):
| Recurso | Pico | Unidad |
|---|---|---|
| FP32 FFMA | 21.7 | TFLOPS |
| FP16 Tensor Core (HMMA) | 174 | TFLOPS |
| INT8 Tensor Core (IMMA) | 696 | TOPS |
| Ancho de banda DRAM | 608 | GB/s |
| Caché L2 | 4 | MB |
| SMs | 48 |
Puntos de Equilibrio Derivados:
| Precisión | Punto de Equilibrio (FLOP/byte) |
|---|---|
| FP32 FFMA | 21700 / 608 = 35.7 |
| FP16 TC | 174000 / 608 = 286.2 |
| INT8 TC | 696000 / 608 = 1144.7 |
- Calcular fracción alcanzada:
attained = effective_throughput / peak_throughput. Si está limitado por memoria: comparar el ancho de banda efectivo con 608 GB/s. Si está limitado por cómputo: comparar GFLOPS efectivos con el pico relevante.
Esperado: Clasificación como limitado por cómputo, memoria o latencia (baja ocupación que causa que ni el cómputo ni la memoria se saturen) con justificación numérica.
En caso de fallo: Recontar bytes. Atención a re-lecturas redundantes (p. ej., 9x en conv2d directo sin im2col). Si ni el cómputo ni la memoria están saturados, el kernel probablemente está limitado por latencia (ver Paso 3).
Paso 3: Calcular la Ocupación
Determinar warps activos por SM a partir de la configuración de lanzamiento y el uso de recursos:
- Extraer uso de recursos:
nvcc --cubin -arch=sm_86 -O2 --resource-usage -o kernel.sm_86.cubin kernel.cu 2>&1 | grep -E 'registers|smem' - Desde la configuración de lanzamiento:
warps_per_block = threads_per_block / 32. - Calcular blocks/SM desde cada factor limitante:
- Límite de registros:
floor(65536 / (registers_per_thread * threads_per_block)) - Límite de smem:
floor(available_smem_per_SM / smem_per_block)-- ver Paso 6 para el acantilado - Límite de warps:
floor(48 / warps_per_block)(GA104 max: 48 warps/SM) - Límite de bloques: 16 bloques/SM máximo en GA104
- Límite de registros:
- Bloques/SM reales =
min(register_limit, smem_limit, warp_limit, block_limit). - Warps activos/SM =
blocks_per_SM * warps_per_block. - Umbral clave: 8 warps/SM es suficiente para ocultar latencia en GA104. Por debajo de 8 = problema estructural que causa comportamiento limitado por latencia.
Esperado: Tabla de ocupación que muestra blocks/SM, warps activos/SM y el factor limitante (registros, smem o warps).
En caso de fallo: Verificar cuFuncSetAttribute para memoria compartida dinámica. Verificar que los reportes de --resource-usage coincidan con la configuración de lanzamiento real. Si el conteo de registros es inesperadamente alto, probar --maxrregcount=N para limitar registros (intercambiando spills de registros por ocupación).
Paso 4: Calcular la Razón Cómputo/Carga Por Tile
Contar instrucciones de cómputo y bytes de carga por K-tile desde SASS (no código fuente):
- Desensamblar:
cuobjdump -sass kernel.sm_86.cubin > kernel.sass - Contar instrucciones de cómputo por tile (el bucle interno sobre un K-tile):
grep -c 'HMMA' kernel.sass-- ops FP16 Tensor Coregrep -c 'IMMA' kernel.sass-- ops INT8 Tensor Coregrep -c 'FFMA' kernel.sass-- multiplicación-suma fusionada FP32
- Contar cargas globales por tile:
grep -c 'LDG' kernel.sass-- cargas de memoria global- Multiplicar por bytes por carga (típicamente 16 bytes para LDG.128)
- Calcular razón:
compute_ops / load_opspor tile. - Clasificar usando el umbral de decisión cp.async (de gpu_reflections.md Insight 2):
- Alto (>20:1): cp.async es netamente negativo; warp interleaving ya oculta la latencia DRAM. Enfocarse en cambios algorítmicos. Referencia: Flash Attention tiene 64 HMMA por tile = razón alta, cp.async medido -5%.
- Medio (5-20:1): cp.async puede ayudar, hacer benchmark de ambas rutas.
- Bajo (<5:1): cp.async fuertemente beneficioso; las cargas dominan y la copia asíncrona oculta la latencia. Referencia: IGEMM tiene 8 IMMA por tile = razón baja, cp.async medido +35%.
Esperado: Razón cómputo/carga con clasificación (alto/medio/bajo) y recomendación de cp.async.
En caso de fallo: Contar desde el desensamblado SASS, no desde el código fuente -- el compilador puede fusionar, eliminar o reordenar instrucciones. Asegurarse de contar instrucciones únicamente dentro del bucle interno (la iteración del K-tile), no del kernel completo.
Paso 5: Inspeccionar Instrucciones SASS
Examinar la mezcla completa de instrucciones SASS y los códigos de stall:
- Desensamblar (si no se hizo en el Paso 4):
cuobjdump -sass kernel.sm_86.cubin > kernel.sass - Contar tipos de instrucciones clave:
grep -c 'HMMA.16816' kernel.sass # FP16 Tensor Core grep -c 'IMMA.16816' kernel.sass # INT8 Tensor Core grep -c 'FFMA' kernel.sass # FP32 fused multiply-add grep -c 'LDGSTS' kernel.sass # cp.async (global->shared) grep -c 'LDG' kernel.sass # Global load grep -c 'STS' kernel.sass # Shared store grep -c 'LDS' kernel.sass # Shared load grep -c 'BAR.SYNC' kernel.sass # Barrier synchronization grep -c 'SHFL' kernel.sass # Warp shuffle (reductions) grep -c 'MUFU' kernel.sass # Special function unit - Verificar códigos de stall en instrucciones críticas:
grep 'HMMA' kernel.sass | head -5 # Expect S08 minimum (hardware constraint) grep 'IMMA' kernel.sass | head -5 # Compiler emits S04, reducible to S02 via CuAssembler grep 'FFMA' kernel.sass | head -5 # Check for S04 (reducible to S01 on independent FFMAs) - Identificar objetivos de optimización:
- Stalls HMMA S08: mínimo de hardware en Ampere, no se puede reducir. Enfocarse en otra parte.
- Stalls IMMA S04: el compilador es conservador. CuAssembler puede ajustar a S02 (15-20% de ganancia medida).
- Stalls FFMA S04: si son independientes, reducibles a S01 vía CuAssembler.
- BAR.SYNC excesivo: puede indicar sobre-sincronización entre etapas del pipeline.
Esperado: Tabla de conteo de instrucciones y resumen de códigos de stall con objetivos de optimización identificados.
En caso de fallo: Asegurar que la arquitectura de cuobjdump coincide con el objetivo de compilación del kernel (ambos deben ser sm_86). Si la salida SASS está vacía, el cubin puede estar corrupto -- recompilar.
Paso 6: Verificar el Acantilado de Smem
Determinar si el uso de memoria compartida cruza el acantilado de ocupación específico de la arquitectura:
- Leer smem/block desde la salida de
--resource-usage(Paso 3) ocuobjdump --res-usage kernel.sm_86.cubin. - Comparar con el umbral del acantilado:
- GA104 (sm_86): 100 KB max smem/SM. Acantilado en 50 KB/block.
- Confirmado empíricamente: 48 KB/block -> 2 blocks/SM (bueno), 56 KB/block -> 1 block/SM (regresión 2x).
- Si está por encima del acantilado (smem > 50 KB/block):
- Blocks/SM cae a 1, warps activos caen a warps_per_block (típicamente 4).
- Se espera regresión de rendimiento 2x por stalls de DRAM expuestos.
- Verificar el impacto del double-buffering: El double-buffering duplica el uso de smem. Si la smem actual es 30 KB, double-buffered = 60 KB, lo cual cruza el acantilado. Evaluar si el beneficio asíncrono supera la pérdida de ocupación.
- Registrar smem/block, blocks/SM y si el acantilado se cruza.
Esperado: Valor smem/block con conteo blocks/SM y declaración explícita de si el acantilado de 50 KB se cruza.
En caso de fallo: Si está por encima del acantilado y la ocupación es el cuello de botella, la estrategia de optimización debe cambiar: reducir el tamaño del tile para tener smem por debajo de 50 KB, o aceptar 1 block/SM y compensar con una razón cómputo/carga más alta por tile (más reuso de registros, K-tiles más largos).
Paso 7: Construir la Matriz de Decisión
Sintetizar los hallazgos de los Pasos 2-6 en una estrategia de optimización:
| Condición | Estrategia |
|---|---|
| Limitado por memoria + razón cómputo/carga baja (<5:1) + smem bajo el acantilado | Software pipelining con cp.async (LDGSTS). Solapar cargas globales con cómputo. |
| Limitado por memoria + razón cómputo/carga alta (>20:1) + 8+ warps | Warp interleaving ya oculta latencia. Enfocarse en cambios algorítmicos: implicit GEMM, split-Q, im2col. |
| Limitado por cómputo + pesado en FFMA | Ajuste de códigos de stall con CuAssembler: S04 -> S01 en FFMAs independientes. |
| Limitado por cómputo + pesado en HMMA | S08 es mínimo de hardware, no se puede reducir. Aumentar reuso de tile (tiles M/N más grandes, K-loop más largo). |
| Limitado por cómputo + pesado en IMMA | CuAssembler: S04 -> S02 en instrucciones IMMA (el compilador es conservador). |
| Limitado por latencia (baja ocupación, ninguno saturado) | Reducir smem o registros para obtener más blocks/SM. Subir por encima de 8 warps/SM. |
| Smem por encima del acantilado | Reducir el tamaño del tile o reestructurar para tener smem/block bajo 50 KB (GA104). |
- Clasificar las estrategias aplicables por ganancia esperada, usando los datos de razón cómputo/carga y ocupación.
- Estimar el rango de ganancia para cada estrategia basándose en cuán lejos está el kernel del techo relevante.
- Marcar conflictos: p. ej., cp.async duplica smem (puede cruzar el acantilado), tiles más grandes aumentan la presión de registros (puede reducir la ocupación).
Esperado: Lista clasificada de optimizaciones recomendadas con rango de ganancia predicho y conflictos potenciales.
En caso de fallo: Si no emerge un ganador claro, ejecutar micro-benchmarks aislando cada estrategia (p. ej., probar cp.async solo, probar tamaño de tile reducido solo) para medir el impacto real antes de combinar.
Paso 8: Documentar Hallazgos
Producir un reporte estructurado del cuello de botella:
- Línea base: tiempo del kernel, GFLOPS efectivos, ancho de banda efectivo, dimensiones del problema.
- Posición en el roofline: intensidad aritmética, clasificación, fracción alcanzada del pico.
- Ocupación: blocks/SM, warps activos/SM, factor limitante.
- Razón cómputo/carga: valor de la razón, clasificación (alto/medio/bajo), recomendación de cp.async.
- Resumen SASS: tabla de conteo de instrucciones, hallazgos de códigos de stall, objetivos de CuAssembler.
- Acantilado smem: smem/block, blocks/SM, estado del acantilado.
- Recomendación: estrategias de optimización clasificadas con estimaciones de ganancia.
## Bottleneck Analysis Report: [kernel_name]
### Baseline
- Problem: [dimensions]
- Kernel time: [X] ms
- Effective GFLOPS: [Y] | Effective BW: [Z] GB/s
### Roofline Classification
- Arithmetic intensity: [AI] FLOP/byte
- Balance point: [BP] FLOP/byte ([precision])
- Classification: **[compute|memory|latency]-bound**
- Attained fraction: [X]% of peak
### Occupancy
| Resource | Per Block | Limit/SM | Blocks/SM |
|----------|-----------|----------|-----------|
| Registers | [N]/thread | 65536 | [B] |
| Shared mem | [X] KB | 100 KB (cliff: 50 KB) | [B] |
| Warps | [W] | 48 | [B] |
| **Limiting** | | | **[min(B)]** |
- Active warps/SM: [W] ([sufficient|insufficient] for latency hiding)
### Compute/Load Ratio
- Compute ops/tile: [N] [HMMA|IMMA|FFMA]
- Load bytes/tile: [N] bytes ([N] LDG x [N] bytes)
- Ratio: [X]:1 — **[high|medium|low]**
- cp.async recommendation: [beneficial|neutral|detrimental]
### SASS Instruction Mix
| Instruction | Count | Notes |
|-------------|-------|-------|
| HMMA.16816 | [N] | Stall: S08 (hardware min) |
| IMMA.16816 | [N] | Stall: S04 (reducible to S02) |
| FFMA | [N] | Stall: S04 (reducible to S01) |
| LDG | [N] | |
| LDGSTS | [N] | cp.async |
| BAR.SYNC | [N] | |
### Smem Cliff
- Smem/block: [X] KB — [under|over] 50 KB cliff
- Blocks/SM: [B] — [no occupancy loss|occupancy halved]
### Recommended Optimizations (ranked)
1. [Strategy] — estimated [X-Y]% gain
2. [Strategy] — estimated [X-Y]% gain
3. [Strategy] — estimated [X-Y]% gain
Esperado: Reporte markdown completo consumible por un agente kernel-optimizer o desarrollador humano.
En caso de fallo: Re-ejecutar con diferentes tamaños de problema (p. ej., 1024, 2048, 4096, 8192) para confirmar que los hallazgos no son específicos del tamaño. Problemas pequeños pueden parecer limitados por latencia cuando el cuello de botella real a escala es el ancho de banda de memoria.
Validación
- Línea base medida con eventos CUDA (no reloj de pared)
- Clasificación en el roofline determinada (limitado por cómputo/memoria/latencia)
- Ocupación calculada con factor limitante identificado
- Razón cómputo/carga por tile calculada desde SASS
- Mezcla de instrucciones SASS y códigos de stall documentados
- Acantilado smem verificado contra el umbral de la arquitectura
- Matriz de decisión aplicada con recomendación de estrategia
- Hallazgos documentados en reporte estructurado
Errores Comunes
- Multiplicación de re-lecturas: Conv2d directo lee cada peso 9x sin im2col, inflando el conteo de bytes 9x. Usar bytes únicos reales cargados desde DRAM, no instrucciones de carga totales, al calcular la intensidad aritmética.
- Confundir el pico FP16 Tensor Core con el pico FP32: El pico FP16 TC es 174 TFLOPS, el pico FP32 FFMA es 21.7 TFLOPS -- una diferencia de 8x. Usar el pico equivocado hace la clasificación del roofline carezca de sentido.
- Usar 64 KB como acantilado smem en lugar de 50 KB en GA104: GA104 (sm_86) tiene 100 KB max smem/SM. El acantilado está en 100/2 = 50 KB/block, no 64 KB. Esto es específico de la arquitectura; otras GPUs difieren.
- Ignorar warp interleaving al evaluar cp.async: 8 warps con fases de cómputo largas (razón cómputo/carga alta) ya ocultan la latencia DRAM mediante el scheduling de warps. Añadir cp.async en este régimen añade presión de smem y sobrecarga de barrera sin beneficio (medido -5% en Flash Attention).
- Contar instrucciones desde código fuente en lugar de SASS: El compilador puede fusionar operaciones, eliminar código muerto, desenrollar bucles de manera diferente o reordenar instrucciones. Siempre contar desde la salida de
cuobjdump -sass. - No ejecutar iteraciones de calentamiento: El primer lanzamiento del kernel incluye sobrecarga de compilación JIT y efectos de caché frío. Siempre ejecutar 2-5 iteraciones de calentamiento antes de la ejecución medida.
Habilidades Relacionadas
pipeline-gpu-kernel-- implementar software pipelining con cp.async cuando el análisis identifica un kernel limitado por memoria con razón cómputo/carga bajasimulate-cpu-architecture-- análisis de arquitectura complementario para cuellos de botella del lado CPU en flujos de trabajo host-device
GitHub リポジトリ
関連スキル
llamaguard
その他LlamaGuardは、暴力やヘイトスピーチなど6つの安全性カテゴリーにおいて、LLMの入力と出力をモデレートするMetaの70-80億パラメータモデルです。94〜95%の精度を提供し、vLLM、Hugging Face、Amazon SageMakerを使用してデプロイ可能です。このスキルを使用して、AIアプリケーションにコンテンツフィルタリングと安全策を簡単に統合できます。
cost-optimization
その他このClaudeスキルは、リソースの適正サイジング、タグ付け戦略、支出分析を通じて、開発者がクラウドコストを最適化することを支援します。AWS、Azure、GCPにわたるクラウド支出の削減とコストガバナンスの実施のためのフレームワークを提供します。インフラコストの分析、リソースの適正サイジング、または予算制約への対応が必要な際にご利用ください。
quantizing-models-bitsandbytes
その他このスキルは、bitsandbytesを使用してLLMを8ビットまたは4ビット精度に量子化し、精度の低下を最小限に抑えつつ50〜75%のメモリ削減を実現します。限られたGPUメモリでより大規模なモデルを実行したり、推論を高速化するのに理想的で、INT8、NF4、FP4などのフォーマットをサポートしています。HuggingFace Transformersと統合され、QLoRAトレーニングや8ビットオプティマイザーを可能にします。
dispatching-parallel-agents
その他このClaudeスキルは、複数のエージェントを配備し、3つ以上の独立した問題を並行して調査・修正します。共有状態や依存関係がなく解決可能な、無関係な障害が発生するシナリオ向けに設計されています。中核となる機能は並列問題解決であり、効率を最大化するために独立した問題領域ごとに1つのエージェントを割り当てます。
