Skip to content

English · Español

Lab 01 — Medir el ancho de banda de la RAM empíricamente

Objetivo: probar que el techo de ancho de banda de DRAM existe, golpeándolo.

Tiempo estimado: 60–90 minutos.

Prerrequisito: el lab 00 (perfil del ordenador) debe estar comprometido primero.


Qué produces

Un directorio experiments/01-memcpy-bandwidth/ que contenga:

  • bench.py — tu script de medición (lo escribe Borja; SIN espiar el código fuente de NumPy).
  • results.json — throughput medido a múltiples tamaños de buffer.
  • bandwidth.png — gráfica de tamaño de buffer vs throughput, eje x logarítmico.
  • manifest.json{seed, versions, config, hardware} según LYNX_CORTEX.md §5.
  • README.md (2–3 párrafos) explicando qué mediste, cómo, y qué te dice la forma de la curva.

El kernel

El "kernel" de este lab es la operación memory-bound más simple posible: copiar un buffer de N valores fp32 a otro buffer de N valores fp32, y cronometrar.

Cada iteración lee 4N bytes y escribe 4N bytes — 8N bytes movidos en total. Throughput en GB/s = 8N / time_seconds / 10⁹.

Quieres medir esto para tamaños de buffer que abarquen L1 → L2 → L3 → DRAM, es decir, desde ~1 KiB hasta ~1 GiB. La gráfica debería mostrar tres mesetas correspondientes a las tres caches, y luego asentarse en el ancho de banda de DRAM.

TODOs

Bloque A — escribe el kernel

  • Usa arrays de numpy (np.empty(N, dtype=np.float32)). NO listas de Python.
  • Usa np.copyto(dst, src) para la copia real. (No escribas tu propio bucle — los bucles de Python sobre fp32 miden overhead del intérprete, no memoria.)
  • Cronometra con time.perf_counter_ns(). Repite cada medición las suficientes veces para que una repetición completa tarde ≥ 100 ms (para sofocar el ruido del temporizador); típicamente 5–500 repeticiones según N.
  • Una iteración de warm-up antes de cronometrar, para poblar caches y evitar page faults.
  • Tamaños de buffer: al menos 12 puntos, log-espaciados desde 1 KiB a 1 GiB. Sugerido: [1, 4, 16, 64, 256, 1024, 4096, 16384, 65536, 262144, 1048576] KiB (es decir, 2^(0..20) KiB).
  • En cada tamaño, registra: bytes_per_iter, iters, elapsed_s, throughput_GBs. Guarda como results.json.

Bloque B — gráfica

  • matplotlib. eje x: tamaño de buffer en KiB, escala log. eje y: throughput en GB/s, lineal.
  • Anota los límites L1/L2/L3 en la gráfica, usando los tamaños de tu profile.md. (Líneas verticales discontinuas + etiquetas.)
  • Anota el techo esperado de DRAM desde profile.md como una línea horizontal discontinua.
  • Guarda como bandwidth.png y referéncialo desde README.md.

Bloque C — interpreta

Tres preguntas a responder en README.md:

  1. ¿A qué tamaño de buffer cae el throughput de forma abrupta? Compara con los tamaños de L1, L2, L3. Deberían alinearse.
  2. ¿Dónde se asienta la curva a la derecha? Ese es tu ancho de banda medido de DRAM. ¿Cómo de cerca está del β_peak teórico del lab 00?
  3. ¿Por qué el throughput a tamaños de buffer pequeños (en L1) es menor que el ancho de banda L1 de ~1 TB/s? (Pista: piensa qué overhead domina una medición de 1 KiB.)

Bloque D — manifest

Esquema de manifest.json:

{
  "experiment": "01-memcpy-bandwidth",
  "date": "YYYY-MM-DD",
  "seed": 42,
  "versions": {
    "python": "3.11.x",
    "numpy": "X.Y.Z",
    "matplotlib": "X.Y.Z",
    "linux_kernel": "..."
  },
  "hardware": {
    "cpu_model": "Intel Core i5-8250U",
    "cores_threads": "4/8",
    "ram_gib": 62,
    "cpu_governor_at_run": "performance"
  },
  "config": {
    "sizes_KiB": [1, 4, 16, 64, 256, 1024, 4096, 16384, 65536, 262144, 1048576],
    "min_elapsed_ms_per_size": 100,
    "warmup_iters": 1
  },
  "results_summary": {
    "peak_GBs_measured": null,
    "L1_plateau_GBs_measured": null,
    "DRAM_floor_GBs_measured": null
  }
}

Rellena results_summary después de graficar.

Restricciones

  • Sin mlflow, sin dvc, sin wandb. La Fase 0 los difirió según §A8. Un directorio + un archivo JSON es el manifest.
  • Sin threading aún. Solo benchmarks single-thread. El ancho de banda multi-hilo es tema de la Fase 35.
  • CPU governor: performance. Ponlo antes de ejecutar: sudo cpupower frequency-set -g performance. Revierte después. Regístralo en el manifest.
  • Ejecuta enchufado a la red, no con batería. La batería throttlea agresivamente.
  • Cierra otras apps. El tráfico de memoria en segundo plano contamina la medición.

Condiciones de parada

Terminado cuando:

  1. El directorio tiene los seis archivos.
  2. bandwidth.png muestra tres mesetas visibles (L1, L2, L3) y una cola en DRAM.
  3. El ancho de banda DRAM medido está dentro del 30% del β_peak del lab 00. (Fuera de ese rango → probablemente el Turbo Boost está apagado o el governor no está configurado; revísalo antes de mirar la solución.)
  4. El README.md responde a las tres preguntas del Bloque C.

Escollos (lee antes de depurar)

  • El throughput a 1 KiB parece 50 GB/s, no 1 TB/s. Sí. El overhead del temporizador domina. Es una característica, no un bug — anótalo en tu README.
  • El throughput vuelve a subir a 1 GiB. Probablemente estás golpeando el cache de archivos del OS u otros artefactos. Re-comprueba con una asignación fresca en cada iteración (o np.copyto(dst, src) donde ambos están pre-asignados).
  • La meseta L2 es invisible. Prueba con tamaños más espaciados cercanos alrededor de L1_size × 1.5 a L2_size × 1.5. La transición es gradual.
  • El ancho de banda está sospechosamente bajo en todas partes. Comprueba cpupower frequency-infopowersave reduce a la mitad tus números.

Cuándo consultar solutions/

Después de haber comprometido tus seis archivos y respondido las preguntas del Bloque C. La solución en solutions/01-memcpy-bandwidth-ref.md (escrita en apertura de fase) compara tus números y la estructura de tu código.


Siguiente lab: lab/02-cache-walks.md.