Moment.js adalah salah satu perpustakaan JavaScript paling populer untuk mem-parsing dan memformat tanggal. WhereTo menggunakan Node.js, jadi bagi mereka, menggunakan perpustakaan ini adalah langkah yang sepenuhnya alami. Masalah dengan penggunaan server dari Moment.js tidak diharapkan. Pada akhirnya, sejak awal, mereka menggunakan perpustakaan ini di frontend untuk menampilkan tanggal dan senang dengan pekerjaannya. Namun, fakta bahwa perpustakaan berkinerja baik pada klien tidak berarti bahwa tidak akan ada masalah dengan server juga.

Bahan, terjemahan yang kami terbitkan hari ini, didedikasikan untuk kisah pemecahan masalah kinerja Moment.js.
Pertumbuhan proyek dan penurunan produktivitas
Baru-baru ini, jumlah catatan penerbangan yang dikembalikan oleh sistem WhereTo telah meningkat sekitar sepuluh kali lipat. Kemudian kami menghadapi penurunan kinerja yang sangat kuat. Ternyata siklus rendering, yang membutuhkan waktu kurang dari 100 milidetik, sekarang membutuhkan lebih dari 3 detik untuk menampilkan sekitar 5.000 hasil pencarian. Tim kami telah memulai penelitian. Setelah beberapa sesi pembuatan profil, kami perhatikan bahwa lebih dari 99% dari waktu ini dihabiskan dalam satu fungsi yang disebut
createInZone
.
Fungsi createInZone membutuhkan waktu sekitar 3,3 detik untuk selesai.Melanjutkan penyelidikan kami terhadap situasi, kami menemukan bahwa fungsi ini dipanggil oleh fungsi parseZone
parseZone
. Kenapa dia begitu lambat? Kami merasa bahwa perpustakaan Moment.js dirancang untuk skenario penggunaan umum, dan sebagai hasilnya, ia akan mencoba memproses string input dengan berbagai cara. Mungkin Anda harus membatasinya? Setelah kami membaca dokumentasi, kami menemukan bahwa fungsi
parseZone
menerima argumen opsional yang menentukan format tanggal:
moment.parseZone(input, [format])
Hal pertama yang kami lakukan adalah mencoba menggunakan fungsi
parseZone
dengan menyampaikan informasi tentang format tanggal, tetapi ini, seperti yang ditunjukkan oleh tes kinerja, tidak mengarah ke apa pun:
$ 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)
Meskipun sekarang fungsi
parseZone
bekerja sedikit lebih cepat, untuk kebutuhan kita kecepatan ini jelas tidak cukup.
Optimalisasi spesifik proyek
Kami menggunakan Moment.js untuk memilah tanggal yang diambil dari API (Travelport) penyedia kami. Kami menyadari bahwa selalu mengembalikan data dalam format yang sama:
"2019-12-03T14:05:00.000-07:00"
Mengetahui hal ini, kami mulai memahami struktur internal Moment.js untuk (seperti yang kami harapkan) menulis fungsi yang jauh lebih efisien yang menghasilkan hasil yang sama.
Membuat alternatif yang lebih cepat untuk parseZone
Untuk memulai, kami perlu mencari tahu bagaimana objek Moment.js terlihat. Cukup mudah dimengerti:
> 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 }
Langkah selanjutnya adalah instantiate Moment tanpa menggunakan konstruktor:
export function parseTravelportTimestamp(input: string) { const m = {}
Sekarang sepertinya kami memiliki banyak properti dari instance Moment yang dapat kami atur (Saya tidak membahas detail tentang bagaimana kami mengetahui hal ini, tetapi jika Anda melihat kode sumber Moment.js, Anda akan mengerti):
const FAKE = moment() const TRAVELPORT_FORMAT = 'YYYY-MM-DDTHH:mm:ss.SSSSZ' export function parseTravelportTimestamp(input: string) { const m = {}
Langkah terakhir dari pekerjaan kami adalah mencari tahu cara mengurai nilai
offset
cap waktu. Ternyata posisi ini selalu sama di barisan. Sebagai hasilnya, kami dapat mengoptimalkan ini:
function parseTravelportDateOffset(input: string) { const hrs = +input.slice(23, 26) const mins = +input.slice(27, 29) return hrs * 60 + (hrs < 0 ? -mins : mins) }
Inilah yang terjadi setelah kita menyatukan semuanya:
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 = {}
Tes kinerja
Kami menguji kinerja solusi yang dihasilkan menggunakan modul benchmark npm. Berikut ini adalah kode benchmark:
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 = {}
Berikut adalah hasil penelitian kinerja kami:
$ 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
Ternyata, kami berhasil mempercepat analisis cap waktu sekitar 64 kali. Tetapi bagaimana hal ini memengaruhi operasi sistem yang sebenarnya? Inilah yang terjadi sebagai akibat dari profil.
Total waktu eksekusi parseTravelportTimestamp kurang dari 40 ms.Hasilnya sungguh menakjubkan: kami mulai dengan 3,3 detik, pergi ke tanggal parsing, dan datang ke kurang dari 40 milidetik.
Ringkasan
Ketika kami mulai bekerja pada platform kami, kami harus menyelesaikan sejumlah masalah yang mengerikan. Kami hanya tahu bahwa kami mengulangi: "Biarkan dulu berfungsi, tetapi Anda bisa melakukan pengoptimalan nanti".
Selama beberapa tahun terakhir, kompleksitas proyek kami telah berkembang pesat. Untungnya, sekarang kita telah sampai ke tempat di mana kita dapat beralih ke bagian kedua dari "mantra" kita - optimasi.
Solusi perpustakaan membantu proyek sampai ke tempat sekarang. Tetapi kita dihadapkan dengan masalah "perpustakaan". Memecahkannya, kami belajar bahwa dengan menciptakan mekanisme kami sendiri yang berfokus pada kebutuhan kami, kami dapat membuat kode "lebih mudah" dalam hal mengkonsumsi sumber daya sistem dan menghemat waktu yang berharga bagi pengguna kami.
Pembaca yang budiman! Pernahkah Anda mengalami masalah yang serupa dengan yang dibahas dalam artikel ini?
