CommunityProgramación y desarrollogithub.com

magmalake/mojo-data-performance

Escribe código de datos rápido en Mojo y descubre dónde se va el tiempo, desde los bucles por fila de un pipeline hasta los lectores, escritores y códecs subyacentes, y obtén números de referencia fiables que sobrevivan a la verificación.

¿Qué es mojo-data-performance?

Esta habilidad está diseñada para desarrolladores que escriben código de procesamiento de datos en Mojo y necesitan optimizar el rendimiento de sus pipelines, desde bucles por fila hasta operaciones de E/S. Proporciona reglas prácticas y cuantificadas para identificar cuellos de botella: medir el tiempo de decodificación por separado del trabajo por fila, evitar que un solo hilo consuma lotes decodificados por múltiples hilos, y usar punteros sin índice de listas dentro de bucles. También cubre la optimización de lectores, escritores y códecs (como reducir asignaciones y calcular solo lo necesario), y ofrece guías para obtener benchmarks fiables (mediana con percentil 90, compilación previa, control del entorno). Se integra con la habilidad mojo-syntax de Modular y es útil para código que procesa lotes de Arrow, Parquet o Iceberg mediante las bibliotecas arrow-mlake, parquet.mojo, iceberg.mojo, dista u orca. La documentación incluye referencias a casos de estudio, perfiles de medición y advertencias sobre trampas comunes en Mojo.

Compatible con~Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/magmalake/skills/tree/HEAD/mojo-data-performance

Installed? Explore more Programación y desarrollo skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

El tiempo de un pipeline de datos se va a tres lugares: leer y decodificar los datos, el trabajo realizado en cada fila, y la espera (en almacenamiento, en un candado o en un hilo ocupado). La decodificación ya suele estar optimizada en las bibliotecas. El trabajo en cada fila es lo que tú escribes, y es donde un bucle descuidado cuesta 2–3×.

La mayor parte de este archivo trata sobre el código que consume los lotes de un escaneo. Para el lector, escritor o códec subyacente, y para obtener un número en el que puedas confiar, consulta las dos últimas secciones y sus referencias.

Primero, mide dónde se va el tiempo

Rara vez tienes una segunda implementación (Ray, DuckDB, pyarrow) para comparar. Usa el propio escaneo como referencia:

  1. Mide el escaneo solo. Ejecuta el mismo escaneo, con las mismas columnas, filtro y ScanOptions, y descarta los lotes. Esta es la línea base de solo decodificación: ningún pipeline puede ser más rápido que esto.
  2. Mide tu trabajo por separado. Envuelve la llamada por lote (fold, add, lo que consuma el lote) en perf_counter_ns() y suma los tiempos.
  3. Compáralos. En un solo hilo, un tiempo total dentro de aproximadamente 1.5× del tiempo de solo decodificación significa que el trabajo es barato; por encima de eso, el trabajo es el cuello de botella y merece la pena optimizarlo. Con muchos hilos, verifica también si el trabajo se ejecuta en un solo hilo mientras la decodificación se ejecuta en muchos.

El ejemplo medido: un group-by sobre 77M viajes tomó 4.6 s en un hilo, de los cuales 2.1 s fue el trabajo por fila (45%). En 8 hilos tomó 2.6 s, de los cuales los mismos 2.1 s fueron el trabajo (82%), porque un hilo consumió cada lote. references/profiling.md tiene la receta y sus trampas.

Reglas para el bucle por fila

Cada regla viene con lo que ahorró en el benchmark de shuffle de dista (77M filas, tabla local, un hilo). El código está en references/hot-loops.md.

ReglaEfecto medido
Toma el puntero crudo de cada columna una vez por lote, luego indexa el puntero. No indexes List o Tuple[List, List] dentro del bucle.1.40 → 0.78 s para todo el trabajo por fila
Mantén todo lo que una clave acumula lado a lado en una sola ranura (array de estructuras), no en arrays paralelos, para que una actualización toque una sola línea de caché.2.09 → 1.40 s, junto con la siguiente regla
Filtra sin bifurcaciones: calcula cada fila, escríbela en un índice kept, y avanza kept en Int(keep). Luego suma las filas mantenidas en un segundo bucle ajustado.medido junto con la regla anterior
Cuando las claves son enteros pequeños (zone × zone × hour es 1.7M), indexa un array denso por clave en lugar de usar un Dict.no medido por separado; ambas versiones usaron un array denso
No cambies el orden de las sumas de punto flotante mientras optimizas. Entonces la respuesta debe coincidir con la anterior bit por bit, lo que es una verificación de corrección gratuita.cada paso anterior coincidió exactamente

Reglas para todo el pipeline

ReglaEfecto medido
No dejes que un hilo consuma lo que muchos hilos decodifican. Dale a cada trabajador su propio acumulador y combínalos al final, como hacen las tareas map de dista.16 hilos solo llevaron un group-by de un proceso de 202 s a 126 s en AWS
Mantén varias lecturas en vuelo, incluso con un solo hilo de decodificación: ScanOptions.files_at_once (y overlap_io) en iceberg.mojo.6.6–6.9× en un hilo leyendo desde S3
Carga una biblioteca C compartida una vez por proceso y mantén el manejador, nunca una vez por llamada.con zstd abierto en cada llamada, un escaneo pasó el 52% de su tiempo en dyld; solucionado, 2,687 → 262 ms
Almacena en caché cualquier cosa que parsees y que se repita byte por byte, como esquemas Avro en manifiestos.el costo fijo de planificación de manifiestos cayó 3.6× (113.7 → 31.9 µs)
No asignes por registro: decodifica en ranuras reutilizadas.el lector Avro asignaba alrededor de 60 veces por registro antes de su cursor
Conoce el techo: más allá de unos pocos núcleos, un escaneo está limitado por el ancho de banda de memoria, y más hilos no ayudarán. Lee menos bytes en su lugar.un escaneo Iceberg tomó 35.8 ms en un núcleo y 12.1 ms en cuatro, aproximadamente 3×

references/case-studies.md cuenta cada una de estas historias, con lo que delató el problema.

Lectores, escritores y códecs

Cuando la decodificación misma es la parte lenta, las reglas tratan principalmente de no hacer trabajo que se descarta. Los detalles y sus números están en references/readers-and-codecs.md.

  • Verifica que se pueda cumplir la condición de cada ruta rápida por parte de las llamadas que la alcanzan, y demuestra mediante instrumentación que se ejecuta. Una ruta vectorizada cuya condición ninguna columna de lista podía cumplir dejó una columna de 9 KiB al 43% de una lectura.
  • No calcules lo que una decisión posterior normalmente descarta. Construir un diccionario solo para elegir codificación simple fue el 67% de una escritura de columna int64; eliminarlo hizo la escritura 3.3× más rápida con una salida byte-idéntica.
  • Pide prestado en lugar de copiar, y decodifica directamente en el diseño final, como los niveles de definición en el bitmap de validez.
  • Trabaja en lotes donde los datos los tengan: con 1% de nulos, alrededor de 250 memmoves reemplazan alrededor de 25,000 bifurcaciones por ranura.
  • Reconoce cuándo una etapa ya está al ancho de banda de memoria: una que movió 18.3 MB adentro y afuera en 1.2 ms, alrededor de 30 GB/s, solo podía omitirse.
  • Encuentra el elemento más lento antes de añadir hilos. Dividir por bloque de columna limitó una lectura a 1.39×, porque una columna grande se ejecutó todo el tiempo.

Prueba la optimización tanto como la respuesta: rómpela a propósito y verifica que una prueba falla (references/testing-optimizations.md). Las trampas propias de Mojo, como valores que mueren en su último uso y MutUntrackedOrigin, están en references/mojo-hazards.md.

Obtener un número en el que puedas confiar

references/measurement.md tiene las reglas completas. Las que cambiaron conclusiones en magmalake:

  • Reporta la mediana (p50) con el p90 a su lado, nunca solo la media. Una primera llamada lenta movió la media un 30% y la mediana nada.
  • Construye el benchmark con anticipación; nunca lo cronometres bajo mojo run. El JIT infló un benchmark 1.9×.
  • Verifica que la máquina se mantuvo estable. El mismo benchmark tomó 31 ms inactivo y 89 ms junto a 16 hilos ocupados.
  • Nombra la API y el número de hilos de la referencia. pq.read_table es el escáner de datasets de pyarrow, no la ruta de lectura de parquet-cpp, y la configuración de hilos de pyarrow movió una lectura de archivo 2.9×.
  • Cuando dos números no coinciden, verifica que se produjeron de la misma manera antes de explicar la diferencia.

Antes de afirmar una aceleración

  • Compara las respuestas antes y después, exactamente donde puedas (consulta la última regla del bucle). Una respuesta incorrecta más rápida es peor que una lenta correcta.
  • Reporta ambos números y la configuración: filas, hilos, disco local o S3, y si la caché estaba caliente.
  • Di lo que queda. Después de la corrección del shuffle, 325 ms de los 777 ms siguen siendo copia de columnas, y con muchos hilos el trabajo sigue estando en un solo hilo.

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

magmalake/dista

Write and run distributed Mojo programs with dista, magmalake's Ray-style task framework, where one compiled binary is the driver, the head and every worker. Use when writing or reviewing Mojo code that imports dista or mentions distributed Mojo, a remote task, `Task`/`Task2`/`TaskMany`/`TaskWith`, `ObjectRef`, `rt.remote`, `rt.get`, `Seq`, an actor with methods or checkpoints, a cluster, a head, workers, `DISTA_ROLE`/`DISTA_HEAD`, the `dista` command (status, drain, scale, timeline, stop), draining, autoscaling, or a dista runtime inside an orca pipeline step; and when a dista job hangs, is refused with a registry mismatch, or fails with a lost result. Pairs with Modular's mojo-syntax skill and magmalake's mojo-data-performance skill.

magmalake/magmalake-apps

Build a data application on magmalake as one Mojo project, with pipelines run by orca, a lake of Iceberg tables over S3, and a long-running flare web app reading them, then deploy it to AWS with `orca aws`. Use when starting a new magmalake project or laying one out (pixi, mojoshelf, `bin/`, `pipelines/`, `src/`); when a pipeline step writes Iceberg tables that an app reads; when writing a flare web app, console or dashboard over the lake; when running the project locally with Postgres and a local warehouse; or when deploying to AWS (`orca aws up`, `push`, `register`, `app`, `status`, `down`), setting up its hostnames, secrets, public bucket or costs. Pipelines in depth (partitions, keyed, sensors, dista tasks) are the `orca` skill's.

Skills relacionados