Cambios de zona suavizados

Gana FxDart

Requisito

Un sensor de temperatura reporta doce lecturas crudas al día. Suavízalas con una media móvil de 3 lecturas, clasifica cada valor suavizado en una zona (cool < 20° ≤ ok < 25° ≤ hot) y reporta cada transición de zona: de qué zona sale, a cuál entra y los valores suavizados de ambos lados. Un día sin transiciones imprime una única línea stable en lugar de nada. Los datos de dos días de julio están en el código; ambas versiones deben imprimir las líneas mostradas bajo Salida esperada.

Salida esperada
2026-07-14 (3-reading moving average):
  cool → ok (avg 18.6 → 21.2)
  ok → hot (avg 21.2 → 25.6)
  hot → ok (avg 25.6 → 24.9)
2026-07-15 (3-reading moving average):
  stable — no zone changes

Lado a lado

Dart nativo

FxDart

Por qué difieren

Cada etapa de esta tarea necesita ver elementos vecinos, y ahí es exactamente donde a Dart puro se le acaba el vocabulario: no hay ventana deslizante, ni deduplicación adyacente, ni emparejamiento con el sucesor — ni en la biblioteca estándar ni en package:collection (slices divide sin solaparse). Así que la versión nativa son tres bucles con índice, cada uno cargando su propio estado mutable: una suma por ventana, una lista runStarts comparada contra su propia cola y una mirada atrás con i - 1 para las líneas de transición, más un parche final con isEmpty para el día estable.

La cadena de FxDart enuncia las cinco etapas en el orden en que fluyen los datos: windowed(3)average por ventana, uniqAdjacentBy(zone) conserva el primer valor suavizado de cada racha de zona, pairwise convierte los inicios de racha en transiciones (from, to), y ifEmpty aporta la línea del día estable dentro del pipeline en lugar de un if después. Cada fragmento se puede probar por separado y nada reimplementa límites de ventana. Estos cuatro operadores son adaptaciones al modelo pull de la familia de ventanas de Rx.

Benchmark

Apple M1 Max, 32 GB de RAM · Dart 3.12.2 (compilado AOT) · 2026-08-24

N = 100

Tiempo Empate

Dart nativo 2.3 µs
FxDart 4.3 µs

Memoria pico Empate

Dart nativo 16.4 MB
FxDart 16.3 MB

N = 10,000

Tiempo Empate

Dart nativo 194 µs
FxDart 410 µs

Memoria pico Empate

Dart nativo 22.5 MB
FxDart 22.7 MB

N = 1,000,000

Tiempo Gana FxDart

Dart nativo 39.4 ms
FxDart 37.4 ms

Memoria pico Gana FxDart

Dart nativo 237.3 MB
FxDart 81.8 MB

Las barras son medianas de iteraciones cronometradas repetidas en procesos nuevos por lado (los N pequeños se agrupan por resolución del temporizador). Dos lados a menos del 5% entre sí — o a menos de 0.6 ms, una diferencia que nadie puede percibir — cuentan como empate; las carreras relativas ajustadas se vuelven a medir hasta 5 veces. En una app, cualquier cosa por debajo de unos pocos milisegundos es invisible para el usuario, gane la barra que gane. La memoria es el RSS pico del proceso. La VM de Dart y el dataset son idénticos en ambos lados, así que la diferencia entre las dos barras es lo que retiene el pipeline en sí.