Los 3 gastos más grandes
Requisito
De un mes de gastos, imprime los tres más grandes — comercio e importe, de mayor a menor. Los datos están en el código de abajo; ambas versiones deben imprimir las líneas que aparecen bajo Salida esperada.
Salida esperada
Airline Ticket $289.99 New Headphones $129.00 Electric Co $60.34
Lado a lado
Dart nativo
FxDart
Por qué difieren
Apenas difieren — esto es un empate. Ambos lados ordenan por una clave
negada para conseguir orden descendente y toman los tres primeros; el
sortedBy de package:collection es igual de
directo que el sortBy de FxDart (el List.sort
del núcleo, por sí solo, mutaría en el sitio y necesitaría un
comparador explícito, pero collection es una dependencia
estándar). La única diferencia real está en dónde vive el vocabulario:
un método de extensión de un paquete frente a un paso de una cadena
que además ofrece scan, chunk y variantes
asíncronas. Elige cualquiera de los dos con la conciencia tranquila.
El listado empata; el reloj no. Con un millón de filas las barras de abajo tienen a FxDart unas 2,6× más rápido (200 ms frente a 521 ms), y no se debe a un big-O más listo — ambos copian la lista y ejecutan una mezcla estable O(n log n). La diferencia está en lo que cuesta una sola comparación.
El sortedBy nativo es el mergeSortBy de
collection: ordena las filas y llama al extractor de clave
dentro de cada comparación. Un millón de filas son unas
veinte millones de llamadas a (t) => -t.amount. El tipo
de clave es un K extends Comparable genérico — aquí
num — así que cada una de esas claves es un
double encajonado en el montículo comparado por un
compareTo virtual.
El sortBy de FxDart extrae primero. Un recorrido de la
lista escribe cada clave en un Float64List (vio que
todas eran double). Luego mezcla las claves y las filas
juntas, en orden: cada comparación son dos dobles de máquina
de un array tipado, con una comparación de la VM en estos datos (los
importes son positivos finitos ordinarios, así que no hay NaN ni
-0.0 que fuerce el camino más lento de
compareTo). Una extracción por fila, sin encajonar, sin
despacho y sin perseguir claves por un índice al azar. Un
sortBy anterior decoraba ordenando una lista de índices
con List.sort; eso ya no está. La mezcla es estable por
construcción — las claves iguales conservan su orden de entrada en
ambos lados ahora.
El límite honesto: cuando las claves no son uniformemente
double, int o String,
sortBy recae en el comparador genérico y la ventaja
desaparece. La memoria con un millón de filas está cerca (unos
123 MB frente a 130 MB) — ambos retienen la copia de filas más un
búfer auxiliar. Aquí los importes son todos distintos, así que la
estabilidad no se ve en las tres líneas impresas.
Benchmark
N = 100
Tiempo Empate
Memoria pico Empate
N = 10,000
Tiempo Gana FxDart
Memoria pico Gana FxDart
N = 1,000,000
Tiempo Gana FxDart
Memoria pico Gana nativo
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í.