Enriquecer los principales comercios de forma concurrente

Gana FxDart async

Requisito

A partir de los totales de gasto por comercio de julio, toma los tres comercios principales y enriquece cada uno con su categoría desde una API (simulada) de directorio de comercios —pero la API limita la tasa de peticiones, así que nunca puede haber más de dos consultas en curso a la vez. Los resultados se imprimen por orden de gasto cuando todas las consultas han terminado, y la consulta falsa cuenta las peticiones que se solapan para que ambas versiones puedan demostrar que el límite se respetó. Los datos están en el código de abajo; ambas versiones deben imprimir las líneas que aparecen bajo Salida esperada.

Salida esperada
Green Grocer — $118.65 (Groceries)
Electric Co — $60.34 (Utilities)
Noodle Bar — $54.70 (Dining)
max lookups in flight: 2

Lado a lado

Dart nativo

FxDart

Por qué difieren

La tarea cambia de naturaleza a mitad de camino —primero una clasificación síncrona, luego E/S con límite de tasa— y el código de solo una de las versiones cambia de naturaleza con ella. En FxDart la costura es un único paso de la cadena: sortBy + take eligen los comercios, toAsync cruza al mundo asíncrono, y map + concurrent(2) ejecutan las consultas de dos en dos, en orden. Dart nativo no tiene ninguna primitiva para «como mucho dos en curso»: Future.wait lo dispara todo a la vez, así que la mitad acotada se convierte en un pool de workers hecho a mano —cursor compartido, huecos de resultado predimensionados, futures de worker— que empequeñece la clasificación de dos líneas a la que sirve. Cambiar el límite, o eliminarlo, es un número en la cadena frente a todo ese andamiaje.

Benchmark

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

Caso async: la escala principal es N = 100,000, no 1,000,000. Cada elemento cuesta una vuelta del event loop en ambos lados, así que un millón de awaits reales mediría el event loop de Dart durante minutos — no el pipeline. Los retardos son de longitud cero y se conserva el límite de concurrencia del ejemplo; lo que comparan las barras es la maquinaria del pipeline.

N = 100

Tiempo Empate

Dart nativo 214 µs
FxDart 237 µs

Memoria pico Empate

Dart nativo 16.6 MB
FxDart 16.6 MB

N = 10,000

Tiempo Gana nativo

Dart nativo 17.7 ms
FxDart 21.5 ms

Memoria pico Empate

Dart nativo 49.3 MB
FxDart 50.4 MB

N = 100,000

Tiempo Gana nativo

Dart nativo 195.0 ms
FxDart 233.0 ms

Memoria pico Empate

Dart nativo 81.4 MB
FxDart 84.0 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í.