English · Español
Lab 02 — Test de ancho de banda: H2D, D2H, D2D¶
Objetivo: medir el ancho de banda real de las transferencias de memoria host-a-device, device-a-host y device-a-device; comparar contra los picos teóricos de PCIe y HBM.
Tiempo estimado: 60–90 minutos.
Prerrequisito:
lab/01-device-query.mdcompleto. Los números dedevice_query.jsonson la línea base de comparación.
Lo que produces¶
experiments/23-device-profile/ (extiende lo que hizo el lab 01):
bandwidth_test.py.bandwidth_test.json— throughputs medidos a múltiples tamaños.bandwidth_test.png— plot, tamaño en x (log), throughput en y, tres curvas (H2D, D2H, D2D).interpretation.md— qué te cuenta el plot.
Los kernels¶
Tres transferencias:
- H2D (host-to-device) —
cudaMemcpyAsyncdesde un buffer host pinned (cudaMallocHost) a un buffer device. - D2H (device-to-host) — dirección inversa, los mismos buffers.
- D2D (device-to-device) —
cudaMemcpyAsyncdesde un buffer device a otro, mismo device.
Tamaños: al menos 12 puntos, espaciados en log desde 1 KiB a 1 GiB. Misma escala que el lab 01 de la Fase 1 (memcpy-bandwidth).
TODOs¶
Bloque A — escribir bandwidth_test.py¶
- Reserva buffers host pinned (
cupy.cuda.alloc_pinned_memoryo víacudaHostAlloc). La memoria pinned es necesaria para máximo ancho de banda H2D / D2H; las transferencias desde memoria host pageable van ~la mitad de rápido. - Reserva dos buffers device (1 GiB cada uno).
- Para cada dirección de transferencia y cada tamaño:
- Elige
iterstal que el tiempo total de transferencia ≥ 100 ms. - Warm up: 3 iteraciones antes de cronometrar.
- Sincroniza con
cupy.cuda.runtime.deviceSynchronize()antes y después del bloque cronometrado. - Usa eventos de
cupy.cuda.Streampara cronometrado más fino si puedes;time.perf_counter_nsdespués de sync también vale. - Calcula: throughput = bytes_per_iter × iters / elapsed_s / 1e9 [GB/s].
- Registra en
bandwidth_test.json:
Bloque B — plot¶
- matplotlib. x = tamaño (escala log, KiB), y = GB/s (lineal). Tres curvas con colores distintos y leyenda clara.
- Anota los techos teóricos (líneas horizontales discontinuas):
- Techo H2D / D2H: pico PCIe. Desde
device_query.json:pcie_generation × pcie_lane_width / 8GB/s (muy a grosso modo — PCIe 4.0 x16 = 32 GB/s, PCIe 5.0 x16 = 64 GB/s). - Techo D2D: ancho de banda HBM desde
device_query.json(theoretical_hbm_bandwidth_gbs).
Bloque C — interpretar en interpretation.md¶
Tres párrafos:
- La asimetría H2D/D2H. A menudo están dentro del 10% una de otra si ambas direcciones usan el mismo enlace PCIe. Si son muy diferentes, algo es asimétrico (por ejemplo, calidad PCIe irregular, o una optimización unidireccional en el driver).
- El plateau D2D. ¿En qué tamaño satura D2D? Debería formar plateau al 50–80% de
theoretical_hbm_bandwidth_gbspara tamaños > 1 MiB. (Los tamaños pequeños no pueden saturar porque no generan suficientes transacciones de memoria.) Si el plateau medido es <30% del teórico, algo falla — comprueba que la memoria pinned esté realmente pinned, comprueba que estés sincronizando bien. - Ratio D2D vs H2D. D2D debería ser 30–60× más rápido que H2D en tamaños grandes (HBM bandwidth vs PCIe). Indica el ratio que mediste; compara con el ratio del datasheet.
Bloque D — actualización del manifest¶
Añade al manifest.json existente del lab 01 (o crea una sección nueva):
{
...,
"bandwidth_measured": {
"h2d_peak_GBs": null,
"d2h_peak_GBs": null,
"d2d_peak_GBs": null,
"h2d_pinned_ratio_vs_pageable": null,
"d2d_fraction_of_hbm_peak": null
}
}
(Opcional: mide el ratio pageable-vs-pinned H2D como experimento adicional. Pageable debería ser ~½ de pinned.)
Restricciones¶
- Solo memoria pinned para medidas de pico H2D / D2H. Las transferencias pageable van a ~la mitad del ancho de banda; eso es un experimento separado.
- Sincroniza correctamente. Sin sync estás cronometrando el overhead del lanzamiento del kernel, no la transferencia.
- Una dirección a la vez. H2D y D2H concurrentes (en streams distintos) duplican el throughput agregado; eso es un experimento separado. La Fase 23 mide de una en una.
- GPU única. Las transferencias peer-to-peer multi-GPU (NVLink) son de la Fase 35.
Condiciones de parada¶
Listo cuando:
bandwidth_test.jsontiene las tres curvas con 12+ puntos cada una.bandwidth_test.pngmuestra claramente las tres curvas y las líneas de techo.- El pico D2D está dentro del 30% del ancho de banda teórico de HBM.
- Los picos H2D / D2H están dentro del 30% del ancho de banda teórico de PCIe.
interpretation.mdresponde las tres preguntas del Bloque C con mediciones.- Manifest actualizado.
Trampas¶
- Ruido a tamaños pequeños. A 1 KiB de transferencia, el overhead del lanzamiento del kernel es >50% del cronometrado. No esperes que la curva H2D suba suavemente desde 1 KiB; la curva estará planchada hasta ~64 KiB y entonces sube.
- D2H más lento que H2D. A veces ocurre por asimetría upstream/downstream de PCIe en ciertas plataformas. Anótalo; no intentes "arreglarlo".
- Thermal throttling de la GPU. Las series largas de benchmark pueden throttlear. Mantén las mediciones individuales cortas (menos de 5 segundos cada una).
cudaMemcpyno es realmente async. Es síncrono si cualquiera de los buffers es pageable (no pinned). La versión async requiere ambos pinned.
Cuándo consultar solutions/¶
Tras cumplir las condiciones de parada. La referencia en solutions/02-bandwidth-test-ref.md (escrita al abrir la fase) compara contra los números esperados de la GPU elegida.
Siguiente lab: lab/03-gpu-roofline.md.