IlmHamroh
JavaScript Full-stack/12-qism. JavaScript: ilg'or mavzular va kod sifati32/44-dars21 daqiqa
Mundarija (31)

Performansni o'lchash va benchmarking: performance.now, isitish, mediana va mikro-benchmark tuzoqlari

Qisqacha: Kod tezligini taxmin qilmang — o'lchang. Vaqt performance.now() bilan o'lchanadi (millisekund, kasr qismi bilan). Bitta o'lchovga ishonib bo'lmaydi: dvigatel kodni avval "isitadi" (JIT), GC va boshqa dasturlar shovqin qo'shadi. Shuning uchun avval isitish, keyin ko'p o'lchov va mediana. Mikro-benchmark (bitta kichik amalni o'lchash) ko'pincha aldaydi — dvigatel natijasi ishlatilmagan kodni tashlab yuborishi mumkin. Eng katta yutuq odatda mikro-hiyladan emas, algoritmni almashtirishdan keladi.

Bu darsda

  • performance.now(), console.time va performance.measure bilan kod vaqtini o'lchay olasiz.
  • Nega birinchi o'lchov sekin bo'lishini (JIT isitishi) va nega o'rtacha emas, mediana olinishini tushuntira olasiz.
  • Mikro-benchmarkning to'rt tuzog'ini taniy olasiz: o'lik kodni tashlash, taymer aniqligi, ma'lumotga bog'liqlik va dvigatelga bog'liqlik.
  • Mikro va algoritmik optimizatsiyani farqlay olasiz va "erta optimizatsiya" xavfini tushuntira olasiz.

Oldin bilishingiz kerak: JS dvigateli ichida, Xotira sizishlari va ularni topish, Memoization.

1. Nega bu kerak?

Kursda bu darsga to'rtta va'da bor:

  • Intl.NumberFormat — "tayyor formatlovchi taxminan 70 marta tez";
  • Proxy — "proxy orqali o'qish o'nlab barobar sekin";
  • Currying — "muammo bo'lsa, o'lchab ko'ring";
  • Memoization — "bitta o'lchovga ishonmang, dvigatel kodni hali qizdirmagan bo'ladi".

Bu darsda o'sha raqamlar qanday olinganini va qanday qilib o'zingiz ishonchli raqam olishni o'rganasiz.

Dasturchilar orasida mashhur savollar bor: "for tezmi yoki forEach?", "map sekinmi?". Internetda har biriga o'nlab "tezlik testi" topasiz — va ular bir-biriga zid. Sabab: tezlikni o'lchash — o'z qoidalari bor alohida ko'nikma. Uni bilmasangiz, aniq raqam ko'rib, noto'g'ri xulosa chiqarasiz.

«Bahor»dan o'xshatish. Jasur aka Rustam akaning oshni qancha vaqtda pishirishini bilmoqchi. Birinchi kuni o'lchadi — 3 soat. Lekin u kuni qozon sovuq edi, o'tin nam edi. Ikkinchi kuni — 2 soat. Uchinchi kuni gaz o'chib qoldi — 4 soat. Qaysi biri "haqiqiy"? Bitta kunga ishonib bo'lmaydi. Bir hafta o'lchab, g'alati kunlarni chetga surib, o'rtadagisini olish kerak. Benchmark ham shunday.

2. Vaqtni o'lchash vositalari

2.1 performance.now()

performance.now() — dastur boshlanganidan beri o'tgan vaqtni millisekundda, kasr qismi bilan qaytaradi. Ikki o'qish orasidagi farq — kod bajarilgan vaqt:

js
const boshi = performance.now();
let jami = 0;
for (let i = 0; i < 1_000_000; i++) {
  jami += 35000;
}
const vaqt = performance.now() - boshi;

console.log(typeof vaqt); // number
console.log(vaqt >= 0); // true
console.log(jami); // 35000000000

Aniq millisekundni "Konsolda" ga yozmaymiz — u har kompyuterda va har ishga tushirishda boshqa. Siz o'zingiz «Ishga tushir» bilan console.log(vaqt) qo'shib ko'ring.

Nega Date.now() emas? Ikki sabab. Birinchidan, Date.now() butun millisekund beradi — tez kod uchun 0 yoki 1 dan boshqa narsa ko'rmaysiz. Ikkinchidan, Date.now() kompyuter soatiga bog'liq: soat internet orqali to'g'rilansa, vaqt orqaga "sakrashi" mumkin. performance.now() esa faqat oldinga yuradi — buni monoton soat deyishadi.

2.2 console.time va performance.measure

Tez tekshirish uchun qulayrog'i — console.time. Belgi bilan boshlaysiz, o'sha belgi bilan tugatasiz:

js
// Vaqt har safar boshqacha — shuning uchun tekshirilmaydi
console.time("menyu");
for (let i = 0; i < 1e6; i++);
console.timeLog("menyu", "yarmi"); // oraliq natija
console.timeEnd("menyu");

Node 24.21 dagi bitta ishga tushirish:

text
menyu: 2.212ms yarmi
menyu: 5.905ms

timeLog — oraliq natija (taymer davom etadi), timeEnd — yakuniy natija (taymer o'chadi). Brauzer konsolida format biroz boshqacha bo'lishi mumkin, ma'nosi bir xil. Belgi xato yozilsa (console.timeEnd("menu")), Node ogohlantiradi: Warning: No such label 'menu' for console.timeEnd() — "bunday belgi yo'q".

Uchinchi vosita — performance.mark va performance.measure: kodning ikki nuqtasiga nom qo'yib, orasini o'lchaysiz. Ularning afzalligi — natija Chrome DevTools'ning Performance paneli vaqt chizig'ida ham ko'rinadi (DevTools: Performance, Memory va Network):

js
performance.mark("bron-boshi");
const bronlar = Array.from({ length: 1000 }, (_, i) => i);
performance.mark("bron-oxiri");

const olchov = performance.measure(
  "bronlarni yasash",
  "bron-boshi",
  "bron-oxiri",
);
console.log(olchov.name); // bronlarni yasash
console.log(typeof olchov.duration); // number
console.log(bronlar.length); // 1000

2.3 Taymer aniqligi

Brauzerda performance.now() ataylab qo'polroq qilingan. Juda aniq taymer xavfsizlik teshiklariga yordam berardi: 2018 yildagi Spectre hujumlari protsessor ichidagi mayda vaqt farqlarini o'lchab, boshqa saytning ma'lumotini o'g'irlashga urinardi. Mikrosekund — millisekundning mingdan biri. MDN ma'lumotiga ko'ra Chrome'da aniqlik 100 mikrosekundgacha (0,1 ms) yaxlitlanadi. Maxsus himoyalangan ("cross-origin isolated") sahifalarda — 5 mikrosekund; bunday sahifalarni hozir bilish shart emas.

Buni o'zimiz ko'rdik: Chrome 154 da o'lchangan vaqtlar 0.6999999992549419, 1.099999999627471 kabi chiqdi — ya'ni 0,7 va 1,1 ms: hammasi 0,1 ms ga karrali. Node'da esa 0.0026 ms kabi mikrosekund ulushlari ham ko'rinadi.

Xulosa: juda qisqa amalni bir marta o'lchamang. 0,01 ms davom etadigan funksiyani brauzerda o'lchasangiz, natija 0 yoki 0.1 — hech narsa demaydi. Uni 10 000 marta siklda chaqirib, umumiy vaqtni o'lchang.

Tekshirib ko'ring: Nega taymerni sikl tashqarisida ishga tushiramiz (boshi sikldan oldin), har aylanishda emas?

Javob

Ikki sabab. Birinchisi — aniqlik: bitta aylanish taymer aniqligidan qisqa bo'lishi mumkin, uni o'lchab bo'lmaydi. Ikkinchisi — performance.now() chaqiruvining o'zi ham vaqt oladi. Har aylanishda chaqirilsa, biz kodimizni emas, ko'proq taymerni o'lchagan bo'lamiz.

3. Bitta o'lchov — o'lchov emas

3.1 Isitish (warm-up)

JS dvigateli ichida darsida ko'rdik: funksiya avval Ignition'da sekin bajariladi, keyin "qiziganicha" JIT bilan tezlashadi. Demak, birinchi o'lchov — sovuq qozonda pishirilgan osh. Tekshiramiz: bitta funksiyani ketma-ket 10 marta o'lchaymiz.

js
// isitish.mjs — vaqt har kompyuterda har xil
function chekJami(narxlar) {
  let jami = 0;
  for (const narx of narxlar) {
    jami += narx * 1.12; // QQS bilan
  }
  return jami;
}

const narxlar = Array.from(
  { length: 100_000 },
  (_, i) => 5000 + i,
);
const vaqtlar = [];
for (let i = 0; i < 10; i++) {
  const boshi = performance.now();
  chekJami(narxlar);
  vaqtlar.push(performance.now() - boshi);
}
console.log(vaqtlar.map((v) => v.toFixed(2)).join(" "));

Faylni 7 marta ishga tushirdik. Grafikda — har o'lchov tartib raqami uchun 7 natijaning medianasi:

Bitta funksiya, ketma-ket 10 o'lchov: JIT isitishi
Vaqt, ms
2,870,83110O'lchov tartib raqamichekJami (100 000 narx): 1 → 2,87 mschekJami (100 000 narx): 2 → 1,5 mschekJami (100 000 narx): 3 → 0,99 mschekJami (100 000 narx): 4 → 0,83 mschekJami (100 000 narx): 5 → 0,89 mschekJami (100 000 narx): 6 → 0,91 mschekJami (100 000 narx): 7 → 0,9 mschekJami (100 000 narx): 8 → 0,89 mschekJami (100 000 narx): 9 → 0,88 mschekJami (100 000 narx): 10 → 0,88 ms
Bitta funksiya, ketma-ket 10 o'lchov: JIT isitishi
O'lchov tartib raqamichekJami (100 000 narx)
12,87
21,5
30,99
40,83
50,89
60,91
70,9
80,89
90,88
100,88

Manba: O'lchov: Node 24.21 (V8 13.6), 12th Gen Intel Core i5-12500H, Windows 11, 2026-10-05; 7 marta ishga tushirildi, har nuqta — mediana

Birinchi o'lchov oxirgilardan taxminan 3 barobar sekin. Uchinchisidan boshlab vaqt barqarorlashdi. Agar chekJami ni bir marta o'lchab, boshqa funksiyaning "isigan" vaqti bilan solishtirsangiz — xulosa butunlay noto'g'ri bo'ladi.

Qoida: o'lchashdan oldin funksiyani bir necha (o'nlab) marta "bekorga" chaqiring — bu isitish (warm-up). Keyin o'lchang.

3.2 Mediana, o'rtacha emas

Isitilgan funksiyani ko'p marta o'lchasangiz ham, natijalar biroz sakraydi: shu payt GC ishlab qoldi, brauzer boshqa tabni yangiladi, antivirus faylni tekshirdi. Bitta yomon o'lchov o'rtachani buzadi. Masalan, yuqoridagi 7 ishga tushirishdan birining oxirgi o'lchovlari:

js
function ortacha(sonlar) {
  return sonlar.reduce((a, b) => a + b, 0) / sonlar.length;
}

function mediana(sonlar) {
  const t = sonlar.toSorted((a, b) => a - b);
  const orta = Math.floor(t.length / 2);
  return t.length % 2 ? t[orta] : (t[orta - 1] + t[orta]) / 2;
}

const vaqtlar = [0.88, 0.87, 0.89, 0.88, 4.64, 0.87, 0.9];
console.log(ortacha(vaqtlar).toFixed(2)); // 1.42
console.log(mediana(vaqtlar)); // 0.88

Bitta "g'alati" o'lchov (4.64 — ehtimol shu paytda GC ishlagan) o'rtachani 1,42 ga ko'tardi — bu vaqtni funksiya hech qachon ko'rsatmagan. Mediana — saralangan ro'yxatning o'rtasidagi qiymat — 0.88. U bitta-ikkita chetdagi qiymatga sezgir emas. toSorted — asl massivni o'zgartirmasdan saralangan nusxa qaytaradi (sort va taqqoslash funksiyasi).

mediana ning ishini qadamma-qadam ko'ring:

Ko'p o'lchov kerakligining yana bir sababi — ishga tushirishlar orasidagi farq. JS dvigateli ichida darsida ko'rdik: bir xil fayl 10 ishga tushirishning 6 tasida TurboFan'gacha yetdi, 4 tasida yetmadi. Shuning uchun jiddiy o'lchovda faylning o'zi ham bir necha marta ishga tushiriladi.

3.3 To'liq o'lchov funksiyasi

Hammasini bitta yordamchiga yig'amiz — isitish, ko'p o'lchov, mediana:

js
// olcha.mjs — bu kurs davomidagi o'lchovlarimiz shunday qilingan
export function olcha(funksiya, { isitish = 20, marta = 21 } = {}) {
  for (let i = 0; i < isitish; i++) {
    funksiya();
  }
  const vaqtlar = [];
  for (let i = 0; i < marta; i++) {
    const boshi = performance.now();
    funksiya();
    vaqtlar.push(performance.now() - boshi);
  }
  vaqtlar.sort((a, b) => a - b);
  return vaqtlar[Math.floor(marta / 2)]; // mediana (toq son)
}

Va natija har doim mashina ma'lumoti bilan yoziladi: Node versiyasi, protsessor, operatsion tizim, sana. Bu kursdagi har bir grafikning manba qatoriga qarang — o'sha qoida. Protsessor nomini Node'ning o'zidan olish mumkin:

js
import { cpus } from "node:os";

// masalan: 12th Gen Intel(R) Core(TM) i5-12500H
const model = cpus()[0].model;
console.log(typeof model); // string
console.log(process.version.startsWith("v")); // true

Tekshirib ko'ring: 9 ta o'lchov: [5, 5, 6, 5, 40, 5, 6, 5, 5]. O'rtacha va mediana qancha? Funksiyaning haqiqiy vaqtini qaysi biri yaxshiroq aytadi?

Javob

O'rtacha — 82 ÷ 9 ≈ 9,1. Saralangan: [5, 5, 5, 5, 5, 5, 6, 6, 40], o'rtadagi (5-) — 5. Mediana yaxshiroq: funksiya deyarli har doim 5–6 ms ishlagan, 40 — bitta tasodifiy to'siq.

4. Mikro-benchmark tuzoqlari

Mikro-benchmark — bitta kichik amalni (for va forEach, + va ` `) tezlik uchun solishtirish. U eng ko'p aldaydigan o'lchov turi. To'rt tuzoqni ko'ramiz.

4.1 O'lik kod: dvigatel ishni tashlab yuboradi

Ikki funksiya 10 million marta QQS hisoblaydi. Birinchisida natija hech qayerga ketmaydi, ikkinchisida yig'iladi:

js
// olik-kod.mjs — vaqt har kompyuterda har xil
function qqs(narx) {
  return Math.round(narx * 0.12);
}

function olchaBekor() {
  const boshi = performance.now();
  for (let i = 0; i < 10_000_000; i++) {
    qqs(i); // natija hech qayerga ketmaydi
  }
  return performance.now() - boshi;
}

function olchaTogri() {
  let yigindi = 0;
  const boshi = performance.now();
  for (let i = 0; i < 10_000_000; i++) {
    yigindi += qqs(i); // natija ishlatiladi
  }
  const vaqt = performance.now() - boshi;
  if (yigindi === -1) console.log("hech qachon chiqmaydi");
  return vaqt;
}

Har birini 7 marta o'lchab, medianani oldik; butun faylni 5 marta ishga tushirdik. Natija: olchaBekor — taxminan 4,6 ms, olchaTogri — taxminan 41 ms. Bir xil hisob — 9 barobar farq!

Nima bo'ldi? TurboFan ko'rdi: qqs(i) ning natijasi hech qayerda ishlatilmayapti, va qqs hech qanday yon ta'sir qilmaydi (Pure funksiya). Demak, uni chaqirmasa ham bo'ladi. U chaqiruvni sikldan butunlay olib tashladi — bu o'lik kodni tashlash (dead code elimination). 10 million aylanish 4,6 ms da — bir aylanishga yarim nanosekunddan kam (nanosekund — mikrosekundning mingdan biri). Bu bo'sh siklning tezligi. Biz QQS'ni emas, bo'sh siklni o'lchadik.

Oshxonada bu shunday bo'lardi. Jasur aka Rustam akaning tezligini o'lchamoqchi: "10 ta osh pishir, lekin hech kimga berma". Aqlli oshpaz hech kim yemaydigan oshni pishirmaydi — 10 daqiqadan keyin "tayyor" deydi. Jasur aka esa "10 ta osh — 10 daqiqada!" deb yozib qo'yadi.

Qoida: o'lchanayotgan natijani doim ishlating — yig'ing, oxirida tekshiring yoki qaytaring. if (yigindi === -1) kabi "hech qachon bajarilmaydigan, lekin dvigatel buni bilmaydigan" tekshiruv — eng oddiy usul.

4.2 Natija ma'lumotga bog'liq

Klassik savol: massiv yig'indisi uchun for, for...of, forEach yoki reduce — qaysi tez? Million narxni yig'dik, har usul alohida jarayonda, isitish va 21 o'lchov medianasi bilan, 5 marta. Bir narsani o'zgartirdik — narxlarning o'zi:

Usul narxlar i % 100 narxlar 5000 + i % 30000
for (let i …) ≈ 1,1 ms ≈ 0,9 ms
for...of ≈ 8,5 ms ≈ 8,5 ms
forEach ≈ 7,3 ms ≈ 12,4 ms
reduce ≈ 7,8 ms ≈ 9,5 ms

Kod bir xil — faqat sonlar boshqa — forEach 70 % sekinlashdi. Sabab ikkinchi ustunda: yig'indi Smi chegarasidan oshib ketadi. node --trace-deopt forEach varianti uchun aynan shuni ko'rsatdi: reason: overflow — "to'lib ketish" (JS dvigateli ichida). Haqiqiy ma'lumotingiz qanday bo'lsa — benchmark ham shunday ma'lumot bilan bo'lishi kerak.

4.3 Natija dvigatelga va vaqtga bog'liq

Xuddi shu to'rt usulni Chrome 154 da bitta sahifada besh marta ketma-ket o'lchadik. for...of birinchi ikki marta ≈ 7,6 ms ko'rsatdi — Node'dagiga o'xshash. Uchinchi martadan boshlab esa ≈ 0,9 ms — for bilan deyarli teng! forEach va reduce esa ≈ 8,5 ms da qoldi.

Demak, "for...of 8 barobar sekin" degan xulosa Node 24 ning shu versiyasi va shu vaziyati uchun rost, boshqa dvigatelda yoki keyinroq — yolg'on. V8 ning keyingi versiyasi buni yana o'zgartiradi. Internetdagi "X tezroq" maqolalarining ko'pi aynan shunday — bitta muhit, bitta lahza.

4.4 Haqiqiy kontekst yo'q

Mikro-benchmarkda funksiya yolg'iz, issiq siklda, bir xil ma'lumot bilan aylanadi. Haqiqiy ilovada u boshqa kodlar orasida, turli ma'lumot bilan, kamdan-kam chaqiriladi. Dvigatel ikki holatni butunlay boshqacha optimallashtiradi.

Eng muhim savol: farq qancha? Yuqoridagi jadvalda for va forEach orasidagi eng katta farq — million element uchun taxminan 11 ms. Agar sahifangiz 50 elementli menyuni chizsa — farq mikrosekundning ulushlari. Hech kim sezmaydi. Kodni o'qish qulayligi esa doim seziladi.

Shuning uchun to'g'ri yo'l quyidagicha. Undagi profiler — dastur ishlab turganda vaqt qaysi funksiyaga ketayotganini ko'rsatadigan vosita ("Erta optimizatsiya" bo'limida yana ko'ramiz):

flowchart TD
  A["Kod sekin tuyuldi"] --> B["O'lchang: haqiqiy ma'lumot, isitish, mediana"]
  B --> C{"Muammo haqiqatanmi?"}
  C -- "yo'q" --> D["Hech narsa qilmang"]
  C -- "ha" --> E["Profiler: vaqt qayerga ketyapti?"]
  E --> F["Avval algoritmni o'zgartiring"]
  F --> G["Qayta o'lchang"]

Tekshirib ko'ring: Hamkasbingiz forEach ni hamma joyda for ga almashtirmoqchi, "jadvalda 7 barobar tez" deb. Unga qanday savollar berasiz?

Javob

Uch savol. Bizning ma'lumotlarimiz qancha katta — million elementmi yoki ellikta? O'zgartiriladigan joy haqiqatan sekinmi — o'lchab ko'rdingmi, profiler shu joyni ko'rsatdimi? Va o'lchov boshqa dvigatelda (Chrome, Safari) va haqiqiy ma'lumotda ham shunday chiqdimi? Agar javoblar "yo'q" bo'lsa — o'qilishini yomonlashtirish arzimaydi.

5. Mikro va algoritmik optimizatsiya

Endi boshqa turdagi o'zgarishni o'lchaymiz. Vazifa: kelgan so'rovlarning qaysilari bron qilinganini sanash. Birinchi variant — bronlar massivida includes bilan qidirish. Ikkinchisi — bronlarni bir marta Set ga solib, has bilan:

js
// algoritm.mjs — node algoritm.mjs 10000
const n = Number(process.argv[2]);
const bronlar = Array.from({ length: n }, (_, i) => i * 2);
const sorovlar = Array.from({ length: n }, (_, i) => i);
const bronToplami = new Set(bronlar);

function sanaMassiv() {
  let soni = 0;
  for (const s of sorovlar) if (bronlar.includes(s)) soni++;
  return soni;
}

function sanaSet() {
  let soni = 0;
  for (const s of sorovlar) if (bronToplami.has(s)) soni++;
  return soni;
}

Natija (isitish + 7 o'lchov medianasi, har o'lcham 5 marta ishga tushirildi):

Elementlar (n) includes Set.has
1 000 ≈ 0,85 ms ≈ 0,08 ms
10 000 ≈ 81 ms ≈ 0,43 ms
50 000 ≈ 2 060 ms ≈ 2,4 ms

Bu yerda farq — 10 yoki 70 foiz emas. 50 000 elementda — 850 barobar: ikki soniya va ikki millisekund. Va n o'sgan sari farq o'sib boradi: elementlar 10 marta ko'paysa, includes varianti taxminan 100 marta sekinlashdi, Set esa taxminan 5 marta.

Sabab — har includes butun massivni boshidan qarab chiqadi, has esa javobni deyarli bir qadamda topadi. Bu farqni Set darsida ham ko'rgan edik. Ish hajmining ma'lumot o'lchamiga qarab qanday o'sishini o'lchaydigan til bor — Big-O. Uni Big-O notatsiyasi darsida (algoritmlar qismida) o'rganamiz; hozir bilish shart emas.

Ikki xil optimizatsiyani solishtiring:

Mikro-optimizatsiya Algoritmik optimizatsiya
Misol forEach → for includes → Set.has
Yutuq foizlar, ba'zan bir necha barobar yuzlab, minglab barobar
Barqarorligi dvigatel versiyasiga bog'liq har dvigatelda
O'qilishi ko'pincha yomonlashadi ko'pincha yaxshilanadi

6. Erta optimizatsiya

Kompyuter fanining mashhur olimi Donald Knuth 1974 yildagi maqolasida shunday yozgan: "premature optimization is the root of all evil" ("erta optimizatsiya — hamma yomonlikning ildizi"). U optimizatsiyaga qarshi emas edi. U kodning muhim bo'lmagan joylarini, o'lchamasdan, "tezroq bo'lar" deb murakkablashtirishga qarshi edi.

To'g'ri tartib:

  1. Avval to'g'ri va o'qiladigan kod. Testlar bilan (Birinchi avtomatik test).
  2. Muammo bormi? Foydalanuvchi sekinlikni sezyaptimi? O'lchang.
  3. Qayerda? Profiler (Chrome DevTools Performance paneli, Node'da node --cpu-prof) vaqt qaysi funksiyaga ketayotganini ko'rsatadi. Ko'pincha bu siz o'ylagan joy emas.
  4. Algoritm, keyin mikro. Avval ishni kamaytiring (keshlash, Set, keraksiz qayta chizishni olib tashlash), keyingina mayda hiylalar.
  5. Qayta o'lchang — yaxshilanish haqiqatan bormi.

Rustam aka osh sekin pishyapti deb pichoqni o'tkirlamaydi — avval qayerda vaqt ketayotganini kuzatadi. Ma'lum bo'ladiki, guruchni har safar omborxonadan olib kelish uchun 20 daqiqa ketgan.

7. Ko'p uchraydigan xatolar

7.1 Bitta o'lchov, isitishsiz

console.time → bitta chaqiruv → console.timeEnd → xulosa. Birinchi chaqiruv sovuq, 3 barobar sekin bo'lishi mumkin. Tuzatish: isitish, keyin ko'p o'lchov va mediana — olcha funksiyasi kabi.

7.2 Natijani ishlatmaslik

O'lchanayotgan funksiyaning natijasi tashlab yuborilsa, TurboFan chaqiruvni olib tashlashi mumkin — siz bo'sh siklni o'lchaysiz. Tuzatish: natijani yig'ing va oxirida ishlating.

7.3 Ikki variantni bitta jarayonda ketma-ket o'lchash

Birinchi variant GC "axlatini" qoldiradi, ikkinchisi uni tozalashga vaqt sarflaydi. Yoki ikkalasi bitta umumiy yordamchi funksiya orqali chaqirilib, uning IC'sini polimorfik qiladi (Hidden classes va inline caching). Tuzatish: har variantni alohida jarayonda ishga tushiring yoki tartibni almashtirib ham o'lchang.

7.4 Bir muhitdagi natijani umumlashtirish

"Node'da for...of sekin — demak, brauzerda ham". Chrome'da aksi chiqdi. Tuzatish: natijani o'lchagan muhit bilan birga yozing (versiya, protsessor, sana) va foydalanuvchilaringizning haqiqiy muhitida tekshiring.

7.5 Millisekundlarni "Konsolda" ga yozish

Darsda, hujjatda yoki testda // 5.9ms deb aniq vaqt yozish — boshqa kompyuterda hech qachon takrorlanmaydi. Tuzatish: "taxminan", nisbat ("3 barobar") va o'lchov sharoiti bilan yozing.

8. Mashqlar

1-mashq (oson): Mediana

mediana funksiyasini o'zingiz yozing (darsdagiga qaramay). Juft sonli ro'yxat uchun o'rtadagi ikki qiymatning o'rtachasini qaytarsin. Sinang: [3, 1, 2], [4, 1, 3, 2] va [0.9, 0.8, 7.5, 0.9, 0.8]. Ishora: toSorted va taqqoslash funksiyasi — sort va taqqoslash funksiyasi.

Yechim
js
function mediana(sonlar) {
  const t = sonlar.toSorted((a, b) => a - b);
  const orta = Math.floor(t.length / 2);
  if (t.length % 2 === 1) {
    return t[orta];
  }
  return (t[orta - 1] + t[orta]) / 2;
}

console.log(mediana([3, 1, 2])); // 2
console.log(mediana([4, 1, 3, 2])); // 2.5
console.log(mediana([0.9, 0.8, 7.5, 0.9, 0.8])); // 0.9

(a, b) => a - b muhim: busiz sort sonlarni satr sifatida saralaydi va 10 9 dan oldin keladi. Uchinchi misolda 7.5 — GC'ga tushgan o'lchov — natijaga ta'sir qilmadi.

2-mashq (o'rta): Isitishni o'z ko'zingiz bilan ko'ring

«Isitish» bo'limidagi isitish.mjs ni ko'chiring va 3 marta ishga tushiring. Birinchi o'lchov oxirgisidan necha barobar sekin? Keyin node --jitless isitish.mjs bilan ishga tushiring — farq qoladimi? Ishora: --jitless — JIT'ni butunlay o'chiradi (JS dvigateli ichida).

Yechim

Odatdagi ishga tushirishda birinchi o'lchov oxirgilardan taxminan 3 barobar sekin (bizda 2,9 ms va 0,9 ms). --jitless bilan bizda (3 marta): birinchisi ≈ 11 ms, qolganlari ≈ 8 ms. Ikki xulosa. Birinchidan, hammasi taxminan 9 barobar sekinlashdi — JIT'siz kod faqat Ignition'da ishlaydi. Ikkinchidan, birinchi va keyingi o'lchovlar farqi 3 barobardan 1,3 barobarga tushdi. Demak, isitishning asosiy sababi — JIT. Qolgan kichik farq — birinchi chaqiruvdagi to'liq tahlil va bytecode yasash.

3-mashq (qiyin): olcha bilan solishtiring

«To'liq o'lchov funksiyasi» bo'limidagi olcha ni olcha.mjs ga saqlang. Yangi taqqosla.mjs faylida ikki usulni o'lchang: matnli narxlar massivini (["35000", "28000", …], 100 000 ta) songa aylantirish — map(Number) va map((x) => parseInt(x, 10)). Natija ishlatilsin (o'lik kod bo'lmasin), har usul alohida jarayonda bo'lsin. Natijani mashina ma'lumoti bilan yozing.

Yechim
js
// taqqosla.mjs — node taqqosla.mjs number  |  ... parseint
import { cpus } from "node:os";
import { olcha } from "./olcha.mjs";

const matnlar = Array.from(
  { length: 100_000 },
  (_, i) => String(5000 + (i % 30000)),
);
const usullar = {
  number: () => matnlar.map(Number),
  parseint: () => matnlar.map((x) => parseInt(x, 10)),
};

const nom = process.argv[2];
let tekshiruv = 0;
const vaqt = olcha(() => {
  tekshiruv += usullar[nom]().length; // natija ishlatildi
});
console.log(nom, vaqt.toFixed(2), "ms", tekshiruv > 0);
console.log(process.version, cpus()[0].model);

Git Bash'da har usulni 5 marta:

bash
for i in 1 2 3 4 5; do node taqqosla.mjs number; done
for i in 1 2 3 4 5; do node taqqosla.mjs parseint; done

Har usul uchun o'rtadagi natijani oling. Raqamlaringizni darsdagi qoida bilan yozing: "Node 24.x, , Windows 11, sana; isitish 20, 21 o'lchov medianasi, 5 ishga tushirish". Qaysi tez chiqsa ham, so'ng o'zingizdan so'rang: 100 000 narx uchun farq foydalanuvchi sezadigan darajadami?

4-mashq: Vazifalar qadami — olcha yordamchisi

kurs/mashqlar/12/32-olchov/olcha.mjs ga olcha funksiyasini saqlang va unga ikkinchi qaytariladigan qiymat qo'shing: mediana bilan birga eng kichik va eng katta o'lchov — { mediana, engKichik, engKatta }. Keyin vazifalar dagi kabi 1000 ta vazifa obyektini yarating va ularni Object.groupBy bilan "bajarilgan/faol" ga guruhlashni o'lchang (Memoization darsidagi statistika). Kanon kodga tegmaymiz — bu alohida mashq.

Yechim
js
// kurs/mashqlar/12/32-olchov/olcha.mjs
export function olcha(funksiya, { isitish = 20, marta = 21 } = {}) {
  for (let i = 0; i < isitish; i++) {
    funksiya();
  }
  const vaqtlar = [];
  for (let i = 0; i < marta; i++) {
    const boshi = performance.now();
    funksiya();
    vaqtlar.push(performance.now() - boshi);
  }
  vaqtlar.sort((a, b) => a - b);
  return {
    mediana: vaqtlar[Math.floor(marta / 2)],
    engKichik: vaqtlar[0],
    engKatta: vaqtlar.at(-1),
  };
}
js
// kurs/mashqlar/12/32-olchov/statistika.mjs
import { olcha } from "./olcha.mjs";

const vazifalar = Array.from({ length: 1000 }, (_, i) => ({
  id: i + 1,
  matn: `Vazifa ${i + 1}`,
  bajarildi: i % 3 === 0,
}));

let jami = 0;
const natija = olcha(() => {
  const g = Object.groupBy(vazifalar, (v) =>
    v.bajarildi ? "bajarilgan" : "faol",
  );
  jami += g.faol.length; // natija ishlatildi
});
console.log(natija, jami > 0);

engKichik va engKatta orasidagi farq shovqin qanchalik katta ekanini ko'rsatadi. Farq juda katta bo'lsa — kompyuter band, o'lchovni qayta qiling. 1000 vazifa uchun guruhlash juda tez — bu vazifalar ga memoizatsiya nega kerak emasligining o'lchangan isboti.

bash
git add 12/32-olchov
git commit -m "12/32: olcha yordamchisi va statistika o'lchovi"

9. Real ishda

  • Benchmark kutubxonalari. Isitish, statistika va natija jadvalini o'zi qiladigan tayyor paketlar bor: tinybench (2026-10 da 6.2.0) va mitata (1.0.34). Ular shu darsdagi qoidalarni avtomatlashtiradi. Paketlar bilan ishlashni 16-qismda o'rganamiz.
  • Profiler. Haqiqiy ilovada "qaysi funksiya sekin?" savoliga benchmark emas, profiler javob beradi: Chrome DevTools Performance paneli (DevTools: Performance, Memory va Network) va Node'da node --cpu-prof.
  • Web Vitals. Saytning foydalanuvchi sezadigan tezligi (sahifa qancha tez ochiladi, tugma qancha tez javob beradi) alohida o'lchovlar bilan kuzatiladi — 13-qismda.
  • Intervyu: "Kodni qanday o'lchaysiz?", "Nega birinchi chaqiruv sekin?", "Erta optimizatsiya nima?" — ko'p uchraydi. Algoritmik savollar ("Set yoki massiv?") esa deyarli har intervyuda bor.

Xulosa

  • Vaqt — performance.now() bilan (monoton, kasr ms); tez tekshiruv — console.time; DevTools'da ko'rish — performance.measure. Brauzerda aniqlik ataylab qo'pol (Chrome — 0,1 ms).
  • Isitish: birinchi o'lchov JIT tufayli bizda 3 barobar sekin edi — o'lchashdan oldin funksiyani bir necha marta chaqiring.
  • Mediana, o'rtacha emas: bitta GC pauzasi o'rtachani buzadi; natija mashina ma'lumoti bilan yoziladi.
  • Mikro-benchmark tuzoqlari: o'lik kod tashlanadi (4,6 va 41 ms), natija ma'lumotga va dvigatelga bog'liq (for...of Node'da 8 ms, Chrome'da 0,9 ms).
  • Eng katta yutuq — algoritmdan: includes → Set.has 50 000 elementda 850 barobar; avval to'g'ri kod, keyin o'lchov, keyin optimizatsiya.

Keyingi dars: Toza kod: nomlash — kod sifati bo'limini boshlaymiz: o'zgaruvchi va funksiyalarga yaxshi nom berish, va vazifalar refaktori.

Manbalar

  • MDN: performance.now() ("Reduced time precision" bo'limi), console.time(), Performance.measure() — developer.mozilla.org
  • Node.js hujjatlari: perf_hooks, console.time() — nodejs.org/api
  • Donald E. Knuth, "Structured Programming with go to Statements", ACM Computing Surveys, 1974
  • tinybench — npmjs.com/package/tinybench; mitata — npmjs.com/package/mitata
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Performansni o'lchash va benchmarking: performance.now, isitish, mediana va mikro-benchmark tuzoqlari — IlmHamroh