English · Español
Lab 01 — Strides y vistas¶
Objetivo: ver que
arr.Tes O(1) y quenp.ascontiguousarray(arr.T)es O(n), por medición. Producir, intencionadamente, un bug de aliasing. Crear memoria muscular paraflags.OWNDATA.Tiempo estimado: 45–60 minutos.
Prerrequisitos: lab 00.
Lo que produces¶
Un directorio experiments/06-strides-and-views/ que contiene:
measure.py— tu script de benchmark.aliasing.py— un script de 20 líneas que produce el bug de aliasing intencionadamente e imprime pruebas.results.json— tiempos medidos paraarr.Tfrente anp.ascontiguousarray(arr.T)frente aarr.copy()a lo largo de varios tamaños.transpose-cost.png— gráfico log-log de tiempo frente a tamaño del array para las tres operaciones.manifest.json—{seed, versions, config, hardware}.README.md(3–4 párrafos) — qué mediste, qué demostró el bug de aliasing, tres frases de interpretación.
TODOs¶
Bloque A — medir transpuesta frente a transpuesta-contigua¶
- Reserva
a = np.random.default_rng(42).standard_normal((N, N), dtype=np.float32)paraN ∈ {64, 128, 256, 512, 1024, 2048, 4096}(7 tamaños). - Para cada
N, cronometra tres operaciones (repite cada una ≥10× tras un warm-up): b = a.T(solo reetiquetar)c = np.ascontiguousarray(a.T)(copia con nuevo layout)d = a.copy()(copia sin transponer — debería igualar (2) asintóticamente)- Registra
N, n_bytes (= 4·N²), op, time_ns_per_call. Guarda enresults.json. - Asegura en tu script:
b.base is a,c.base is not a,c.flags.C_CONTIGUOUS is True,b.flags.C_CONTIGUOUS is False.
Bloque B — gráfico¶
- matplotlib. Eje x:
n_bytesen MiB (escala log). Eje y: tiempo en ms (escala log). - Tres líneas:
a.T,ascontiguousarray(a.T),a.copy(). Marcadores + líneas. -
a.Tdebería ser plana cerca de 1 μs (independiente deN). Las otras dos deberían crecer linealmente conn_bytes. - Guarda como
transpose-cost.png.
Bloque C — bug de aliasing, a propósito¶
En aliasing.py:
- Reserva
a = np.arange(20, dtype=np.int32). - Toma
b = a[::2](cada elemento alterno). - Asegura
b.flags.OWNDATA is Falseyb.base is a. - Muta:
b[0] = -999. - Imprime
a— muestra quea[0]es ahora-999. - Luego muestra el patrón seguro:
c = a[::2].copy(). Mutac. Muestra queano se ve afectado. - Escribe un comentario de 1 frase al inicio de
aliasing.pydescribiendo el bug.
Bloque D — demo de as_strided (opcional, exploratorio)¶
Si tienes tiempo, usa numpy.lib.stride_tricks.sliding_window_view para crear una vista de ventana deslizante de longitud 3 sobre un array de 10 elementos. Imprímela. Confirma vía np.shares_memory que comparte el buffer base. No escribas en ella.
Bloque E — manifest¶
Esquema estándar (ver lab 00 para la plantilla). Incluye en config:
"sizes_N": [64, 128, 256, 512, 1024, 2048, 4096],
"ops": ["transpose", "ascontiguousarray_T", "copy"],
"warmup_iters": 1,
"min_repeats": 10
Restricciones¶
- fp32, no fp64. Queremos coincidir con el dtype de tensores de fases posteriores.
- Misma semilla de RNG para todos los tamaños. Reproducibilidad.
- CPU governor
performance. Como en el lab de Fase 1. - Sin threading. Medición monohilo.
Resultados esperados (aproximados, para el i5-8250U)¶
| Op | N=1024 | N=4096 |
|---|---|---|
a.T |
~1 μs | ~1 μs |
np.ascontiguousarray(a.T) |
~3 ms | ~70 ms |
a.copy() |
~2 ms | ~30 ms |
La primera fila debería ser plana. Las otras dos deberían escalar aproximadamente con N². ascontiguousarray(a.T) es más lento que un copy() simple porque la transpuesta recorre el buffer en orden de strides no-unitarios (mal comportamiento de caché).
Condiciones de parada¶
Hecho cuando:
transpose-cost.pngmuestra el contraste plano-frente-a-lineal claramente.aliasing.pycorre de principio a fin y el arrayaimpreso refleja la escritura a través deb.README.mdinterpreta la forma de la curva (una frase por línea: "a.Tes plana porque…", "ascontiguousarray(a.T)escala linealmente porque…", "copy()es ligeramente más rápido porque…").
Escollos¶
- Warmup de JIT. La primera llamada a
np.ascontiguousarraypuede incluir el tiempo de carga de librerías. Corre un warm-up antes del bucle de cronometraje. - GC durante el cronometraje. Envuelve los bloques cronometrados con
gc.disable()/gc.enable()para repetibilidad. - Números engañosos en N grandes por swap. Un fp32 de 4096×4096 son 64 MiB — bien en 62 GiB de RAM. Pero si subes a N=16384 (1 GiB), asegúrate de no estar tocando swap. Vigila
free -h. b = a.T; b[0, 0] = 99debería mutara. Si no lo hace, tomaste una copia accidentalmente en algún sitio. Compruebab.base.- Olvidar la prueba de OWNDATA. El objetivo del bloque de aliasing es hacer el bug visible. Imprime
b.flagsantes de la mutación.
Cuándo consultar solutions/¶
Después de que tu transpose-cost.png exista, tu aliasing.py corra y tu README.md tenga las tres frases de interpretación. Entonces solutions/01-strides-and-views-ref.md (escrito en la apertura de fase) muestra los tiempos de referencia y el recorrido canónico del bug de aliasing.
Siguiente lab: lab/02-broadcasting-trap.md.