Una fuente, dos lectores independientes
Requisito
Una fuente de lecturas debe alimentar dos cómputos independientes — el total y el pico — ejecutándose exactamente una vez. La fuente incrementa un contador cada vez que corre; imprime el total, el pico y el contador para demostrar la pasada única. Los datos están en el código; las dos versiones deben imprimir las líneas que aparecen bajo Salida esperada.
Salida esperada
total: 74 peak: 25 source runs: 1
Lado a lado
RxDart
FxDart
Por qué difieren
Ambos modelos chocan aquí con el mismo muro: sus fuentes se reinician
por consumidor. Escuchar dos veces un stream llano de suscripción
única es un error; iterar dos veces un generador sync*
lo ejecuta dos veces en silencio. Y ambas bibliotecas responden con
la misma idea — compartir una sola pasada. RxDart hace el stream
conectable: publish() difiere la fuente, ambas
reducciones se suscriben, y connect() arranca la única
suscripción que las alimenta. El fork de FxDart
ramifica una sola iteración con buffer: cada fork del mismo objeto
iterable es un cursor independiente sobre un buffer compartido, así
que el cuerpo del generador corre una vez sin importar cuántos
lectores tiren de él.
Las diferencias son textura, no capacidad. La versión rx es sensible
al orden — los lectores deben engancharse antes de
connect(), y un rezagado se pierde eventos; eso es push:
la entrega ocurre estés listo o no. La versión fx es sensible a la
identidad — debes hacer fork del mismo objeto, y un fork que
se rezaga simplemente reproduce el buffer a su propio ritmo; eso es
pull: los valores esperan a la demanda. El coste es memoria: el
buffer compartido retiene cada valor hasta que el fork más lento lo
haya consumido, así que un lector muy rezagado mantiene viva la
pasada entera, donde el rezagado de rx simplemente se la habría
perdido. Cualquiera de las dos es una buena respuesta a «hacer un tee
del pipeline», lo que convierte esto en un empate — elige la que
encaje con el modelo en el que ya vive el resto de tu código.
Benchmark
N = 100
Tiempo Empate
Memoria pico Empate
N = 1,000,000
Tiempo Gana FxDart
Memoria pico Gana FxDart
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í.