Moment.js es una de las bibliotecas JavaScript m谩s populares para analizar y formatear fechas. WhereTo usa Node.js, por lo que para ellos, usar esta biblioteca fue un movimiento completamente natural. No se esperaban problemas con el uso del servidor de Moment.js. Al final, desde el principio, utilizaron esta biblioteca en la interfaz para mostrar fechas y quedaron satisfechos con su trabajo. Sin embargo, el hecho de que la biblioteca funcion贸 bien en el cliente no significaba que tampoco habr铆a problemas con el servidor.

El material, cuya traducci贸n publicamos hoy, est谩 dedicado a la historia de resolver el problema de rendimiento Moment.js.
Crecimiento del proyecto y disminuci贸n de la productividad.
Recientemente, el n煤mero de registros de vuelos devueltos por el sistema WhereTo ha crecido unas diez veces. Luego nos enfrentamos a una fuerte ca铆da en el rendimiento. Result贸 que el ciclo de renderizado, que tom贸 menos de 100 milisegundos, ahora toma m谩s de 3 segundos para mostrar aproximadamente 5,000 resultados de b煤squeda. Nuestro equipo ha comenzado la investigaci贸n. Despu茅s de varias sesiones de creaci贸n de perfiles, notamos que m谩s del 99% de este tiempo se gasta en una sola funci贸n llamada
createInZone
.
La funci贸n createInZone tarda unos 3,3 segundos en completarse.Continuando con nuestra investigaci贸n de la situaci贸n, encontramos que esta funci贸n es llamada por la funci贸n parseZone de
parseZone
. 驴Por qu茅 es tan lenta? Ten铆amos la sensaci贸n de que la biblioteca Moment.js estaba dise帽ada para escenarios de uso comunes y, como resultado, intentar谩 procesar la cadena de entrada de varias maneras. Tal vez deber铆as limitarlo? Despu茅s de leer la documentaci贸n, descubrimos que la funci贸n
parseZone
acepta un argumento opcional que especifica el formato de fecha:
moment.parseZone(input, [format])
Lo primero que hicimos fue intentar usar la funci贸n
parseZone
con pasarle informaci贸n sobre el formato de fecha, pero esto, como lo mostraron las pruebas de rendimiento, no condujo a nada:
$ node bench.js moment#parseZone x 22,999 ops/sec 卤7.57% (68 runs sampled) moment#parseZone (with format) x 30,010 ops/sec 卤8.09% (77 runs sampled)
Aunque ahora la funci贸n
parseZone
funcion贸 un poco m谩s r谩pido, para nuestras necesidades esta velocidad claramente no era suficiente.
Optimizaci贸n espec铆fica del proyecto.
Utilizamos Moment.js para analizar las fechas recuperadas de la API de nuestro proveedor (Travelport). Nos dimos cuenta de que siempre devuelve datos en el mismo formato:
"2019-12-03T14:05:00.000-07:00"
Sabiendo esto, comenzamos a comprender la estructura interna de Moment.js para (como esper谩bamos) escribir una funci贸n mucho m谩s eficiente que produzca los mismos resultados.
Creando una alternativa m谩s r谩pida a parseZone
Para comenzar, necesit谩bamos descubrir c贸mo se ven los objetos Moment.js. Fue bastante f谩cil de entender:
> const m = moment() > console.log(m) Moment { _isAMomentObject: true, _i: '2019-12-03T14:05:00.000-07:00', _f: 'YYYY-MM-DDTHH:mm:ss.SSSSZ', _tzm: -420, _isUTC: true, _pf: { ...snip }, _locale: [object Locale], _d: 2019-12-03T14:05:00.000Z, _isValid: true, _offset: -420 }
El siguiente paso fue crear una instancia de Moment sin usar un constructor:
export function parseTravelportTimestamp(input: string) { const m = {}
Ahora parec铆a que ten铆amos muchas propiedades de la instancia de Moment que podr铆amos configurar (no entramos en detalles sobre c贸mo nos enteramos de esto, pero si observa el c贸digo fuente de Moment.js lo entender谩):
const FAKE = moment() const TRAVELPORT_FORMAT = 'YYYY-MM-DDTHH:mm:ss.SSSSZ' export function parseTravelportTimestamp(input: string) { const m = {}
El 煤ltimo paso de nuestro trabajo fue descubrir c贸mo analizar el valor de
offset
de la marca de tiempo. Result贸 que esta es siempre la misma posici贸n en la l铆nea. Como resultado, pudimos optimizar esto:
function parseTravelportDateOffset(input: string) { const hrs = +input.slice(23, 26) const mins = +input.slice(27, 29) return hrs * 60 + (hrs < 0 ? -mins : mins) }
Esto es lo que sucedi贸 despu茅s de ponerlo todo junto:
const FAKE = moment() const TRAVELPORT_FORMAT = 'YYYY-MM-DDTHH:mm:ss.SSSSZ' function parseTravelportDateOffset(input: string) { const hrs = +input.slice(23, 26) const mins = +input.slice(27, 29) return hrs * 60 + (hrs < 0 ? -mins : mins) } export function parseTravelportTimestamp(input: string): moment { const m = {}
Pruebas de rendimiento
Probamos el rendimiento de la soluci贸n resultante utilizando el m贸dulo de referencia npm. Aqu铆 est谩 el c贸digo de referencia:
const FAKE = moment() const TRAVELPORT_FORMAT = 'YYYY-MM-DDTHH:mm:ss.SSSSZ' function parseTravelportDateOffset(input: string) { const hrs = +input.slice(23, 26) const mins = +input.slice(27, 29) return hrs * 60 + (hrs < 0 ? -mins : mins) } export function parseTravelportTimestamp(input: string): moment { const m = {}
Aqu铆 est谩n los resultados de nuestra investigaci贸n de desempe帽o:
$ node fastMoment.bench.js moment#parseZone x 21,063 ops/sec 卤7.62% (73 runs sampled) moment#parseZone (with format) x 24,620 ops/sec 卤6.11% (71 runs sampled) fast#parseTravelportTimestamp x 1,357,870 ops/sec 卤5.24% (79 runs sampled) Fastest is fast#parseTravelportTimestamp
Al final result贸 que, logramos acelerar el an谩lisis de las marcas de tiempo en aproximadamente 64 veces. Pero, 驴c贸mo afect贸 esto al funcionamiento real del sistema? Esto es lo que sucedi贸 como resultado de la creaci贸n de perfiles.
El tiempo total de ejecuci贸n de parseTravelportTimestamp es inferior a 40 ms.Los resultados fueron simplemente sorprendentes: comenzamos con 3,3 segundos, entrando en fechas de an谩lisis y llegamos a menos de 40 milisegundos.
Resumen
Cuando comenzamos a trabajar en nuestra plataforma, tuvimos que resolver una cantidad terrible de problemas. Solo sab铆amos que nos est谩bamos repitiendo a nosotros mismos: "Deje que funcione primero, pero luego puede hacer la optimizaci贸n".
En los 煤ltimos a帽os, la complejidad de nuestro proyecto ha crecido enormemente. Afortunadamente, ahora hemos llegado al lugar donde podemos pasar a la segunda parte de nuestro "mantra": la optimizaci贸n.
Las soluciones de biblioteca ayudaron al proyecto a llegar a donde est谩 hoy. Pero nos enfrentamos con un problema de "biblioteca". Al resolverlo, aprendimos que al crear nuestro propio mecanismo centrado en nuestras necesidades, podemos hacer que el c贸digo sea "m谩s f谩cil" en t茅rminos de consumo de recursos del sistema y ahorrar tiempo valioso para nuestros usuarios.
Estimados lectores! 驴Ha encontrado problemas similares a los que se analizan en este art铆culo?
