Moment.js vs Native Performance Issues

td; dr: vyhýbejte se výpočtům datumu.jpg ve velkých a hlubokých smyčkách. Viz https://codepen.io/jerrylow/pen/MrjNXK

Před pár měsíci náš tým zabalil náš projekt pro testování. Až do tohoto okamžiku jsme testovali testovací data nebo jen malé vzorky. Všechno fungovalo dobře a byli jsme šťastní, sebevědomí a arogantní (jk). Jakmile QA vyskočila a začala vytvářet větší vzorová data, systém se začal plazit - procházením mám na mysli stěží pohyb. Viděli jsme nahoru odezvu 12 s od serveru a 6 s od klienta. Jednalo se o nejpomalejší dobu odezvy, kterou jsme viděli, byly i jiné části webu, které byly kratší než to, ale rozhodně ne milisekundy, na které jsme byli během vývoje zvyklí.

Náš tým začal vyšetřovat, pak můj tým našel tento článek https://moshen.net/posts/moment_performance_on_node/, který diskutuje o problémech s výkonem s Moment.js - a tak jsme začali testovat. Začali jsme odstraňovat výpočty Moment.js (isBetween, isBefore, sčítat, odčítat ...) ve velkých smyčkách a výsledky byly dramatické. Neviděli jsme přesně 27násobný nárůst, jak je uvedeno v článku, ale dostávali jsme asi 1–2 s dobu odezvy (vs 12 s) z backendu a zhruba stejnou na frontě. Byly provedeny další optimalizace nad Moment.js, aby se zkrátila doba zpracování / doba odezvy.

Pro demonstrační účely

Protože jsme nemohli sdílet aplikace nebo úryvky kódu, vytvořil jsem tento rychlý Codepen, abych demonstroval rozdíl „27krát“ mezi moment.js a nativním: https://codepen.io/jerrylow/pen/MrjNXK screenshot:

Codepen prokáže rozdíl výkonu mezi moment.js vs. nativním.

Když jsem to napsal a sdílel jsem to, věděl jsem, že to bude vyžadovat více kontextu, jak zdůraznil můj kolega ve své odpovědi:

Snažil jsem se ilustrovat obrovský rozdíl mezi používáním metody Moment.js vs nativní metoda Date. Z příkladu se zdá, že 21ms se moc nezdá (a v mnoha případech pravděpodobně cenově dostupný), ale to, co se ve skutečnosti dělo, bylo, že jsme měli nějakou hlubší úroveň hnízdění, například:

const results = arr.map (a => {
  if (moment (a.date) .isBetween (startDate, endDate)) {
    // Více logických věcí
    // Další momentové věci.
  }
});

Najednou se naše milisekundy začaly sčítat - exponenciálně. Takže vidíte, jak bylo zvýšení výkonu „27krát“ tak kritické. V našem případě jsme neprovedli výpočet na 25 000 řádcích dat, ale každý řádek zabere více času než jednoduchý příklad, který jsem vytvořil. Naše aplikace zpracovávala přibližně 3 000–5 000, což bylo v souladu s našimi požadavky uživatelů - takže si nemyslím, že je nemožné, aby někdo pracoval s něčím v datové velikosti 10–20 000.

Takže přestaňte používat Moment.js?

Ne, to vlastně není to, co jsme udělali - Moment.js je stále velmi užitečný. Udělali jsme hybrid. Máme spoustu opakovaně použitelných nativních funkcí, abychom mohli provádět výpočty, jako je sčítání, odčítání, stejný den / měsíc / rok a mezi nimi. Tyto funkce používáme tak, jak jen můžeme, abychom si byli vědomi výkonu, pak používáme Moment.js pro veškeré formátování a některé více kontrolované smyčky, jako je například tisk měsíců v roce. Používáte-li Moment.js pro výpočet dat v rámci rozsáhlých datových sad, může to být hlavní příčina problémů s výkonem, zkuste se zaměnit za nějaký nativní javascript, ale není nutné ho vynechávat z celé aplikace.

Aktualizace - Moment.js v třídění

Tady je něco jiného z tohoto víkendu optimalizace výkonu - Moment.js v třídění. Měli jsme dataset ~ 4k záznamů (není moc v pořádku?) A to bylo seřazeno podle data. Data A a B byla převedena na Moment a porovnána s .isBefore (). Každá konverze a srovnání způsobí, že 4k dat vypadají jako 12k (možná přehání), ale to přineslo dobu vykreslování od 1,8 s do 50 ms.