1. Umumiy ko‘rinish
Katta hajmdagi ma’lumotlar bilan ishlaganda, olingan ma’lumotlarning qanday qayta ishlanishi va ekranda ko‘rsatilishi ba’zan ma’lumotlarning o‘zini olishdan ko‘ra unumdorlikka ko‘proq ta’sir qiladi.
Amaliy loyihalardan birida men o‘n minglab yozuvlarni qayta ishlashi va ularni Grid’da ko‘rsatishi kerak bo‘lgan ekran ishlab chiqdim. Serverdan olingan ma’lumotlarni shunchaki o‘z holicha chiqarish o‘rniga, ekran muayyan mezonlar asosida alohida ma’lumotlar to‘plamlarini bog‘lashi va bir nechta qiymatni qayta ishlash natijasini bitta Cell’da ko‘rsatishi kerak edi.
Ishlab chiqishning dastlabki bosqichlarida unumdorlik muammolari deyarli sezilmadi, chunki testlar nisbatan kichik hajmdagi ma’lumotlar bilan o‘tkazildi. Biroq amaldagi ma’lumotlar hajmi o‘n minglab yozuv yoki undan ko‘pga yetgach, ma’lumotlarni Grid’da aks ettirish uchun zarur bo‘lgan qayta ishlash vaqti uzayib bordi va ma’lumotlar yuklanayotgan paytda boshqa amallar bajarilganda ekran ba’zan qotib qolar edi.
Dastlab bunga Grid’ning o‘zini chizish unumdorligi sabab bo‘lgan deb o‘yladim. Biroq qayta ishlash oqimini tekshirgach, Cell ma’lumotlarini yaratish uchun takroran bajariladigan ma’lumot qidirish va qayta ishlash amallari ham sezilarli xarajat keltirib chiqarayotganini aniqladim.
Ushbu maqolada shu tajribaga asoslanib, katta hajmdagi ma’lumotlarni qayta ishlash paytida yuzaga kelgan unumdorlik muammolarini qanday yaxshilaganimni va bu muammolarning Main Thread Blocking hamda Long Task bilan qanday bog‘liqligini jamlayman.
2. Talablar va muammo holati
Talablar va muammo holati quyidagicha edi.
Talablar
-
Sahifalash o‘rniga skroll qilishdan foydalanib ma’lumotlarni ko‘rsatish
-
Bir necha o‘nlab ustunlar uchun filtrlash funksiyasi talab qilindi (barcha ma’lumotlar kerak edi)
-
Cell qiymatlariga qarab turli fon ranglarini ko‘rsatish
Muammo holati
-
Ma’lumotlarni qabul qilgandan keyin ularni qayta ishlash va ko‘rsatish uchun uzoq vaqt talab qilindi
-
Yuklash paytida ekranda boshqa amallar bajarilganda ekran qotib qolar edi
3. Katta hajmdagi ma’lumotlar bilan takroriy amallar
Dastlabki amalga oshirishda Main Data’dan alohida boshqariladigan Related Data Cell darajasida birlashtirildi.
Soddalashtirilgan ko‘rinishda u quyidagicha edi.
예시)
const columnDefs = [
{
headerName: 'Related Data',
valueGetter: params => {
const relatedItems = relatedData.filter(
item => item.targetId === params.data.id
);
return relatedItems
.map(item => item.name)
.join(', ');
},
},
];
Ma’lumotlar kam bo‘lganida bu muammo emas edi, biroq Main Data va Related Data ikkalasi ham ko‘paygani sari Cell qiymatlarini yaratish uchun zarur bo‘lgan qidiruvlar ham qayta-qayta ko‘paydi.
Ayniqsa, Grid’da ma’lumotlarni yangilash, saralash yoki filtrlash kabi amallarga qarab Cell qiymatlari qayta hisoblanishi mumkin.
Oxir-oqibat muammo shunchaki “Grid’da ma’lumotlar ko‘p edi” degan holat emas edi.
Muammo shunda ediki, ma’lumotlar hajmi oshgani sari ekrandagi har bir elementni ko‘rsatish uchun zarur bo‘lgan qidiruv va qayta ishlash amallari ham takroran bajarilardi.
4. Muammoni Main Thread nuqtayi nazaridan tekshirish
Brauzerning Main Thread’ida nafaqat JavaScript’ni bajarish, balki foydalanuvchi hodisalarini boshqarish va tasvirni chizish uchun zarur bo‘lgan turli vazifalar ham bajariladi.
Shu sababli JavaScript amallari Main Thread’ni uzoq vaqt band qilsa, foydalanuvchi kiritmalari va undan keyingi tasvirni chizish jarayoni ham ta’sirlanishi mumkin.
O‘sha paytda ma’lumotlarni qabul qilish va ularni amaldagi ekranda ko‘rsatish uchun quyidagi vazifalar talab qilinardi.
API javobi
↓
Ma’lumotlarni qidirish
↓
Ma’lumotlarni birlashtirish va o‘zgartirish
↓
Grid ma’lumotlarini yaratish
↓
Grid’ni chizish
↓
Layout / Paint
Har bir amalning o‘zi katta bo‘lmagan bo‘lsa-da, ularni o‘n minglab yozuvlar uchun takrorlash JavaScript’ni bajarishning umumiy xarajatini oshirdi.
Bu muammo Long Task bilan ham bog‘liq. Brauzerlarda Main Thread’da 50 ms dan ko‘proq ishlaydigan Task Long Task deb tasniflanadi.
Agar Long Task bajarilayotgan paytda foydalanuvchi o‘zaro aloqasi yuz bersa, hodisani qayta ishlash ushbu vazifa tugaguncha kechikishi mumkin.
Shu sababli katta hajmdagi ma’lumotlar bilan bog‘liq unumdorlik muammolarini nafaqat umumiy qayta ishlash vaqtini qisqartirish, balki Main Thread’da bir vaqtning o‘zida bajariladigan ish hajmini kamaytirish nuqtayi nazaridan ham ko‘rib chiqish kerak edi.
5. Ma’lumotlarni oldindan qayta ishlash
Avvalo, Cell ma’lumotlarini yaratish vaqtida takroran bajariladigan ma’lumot qidiruvlarini kamaytirishga harakat qildim.
Avval Cell qiymati har safar hisoblanganda Related Data ichidan joriy Row’ga tegishli ma’lumotlarni topish uchun filter() ishlatilardi.
Agar Main Data va Related Data mos ravishda N va M ta yozuvni o‘z ichiga olsa, har bir Cell qiymatini hisoblash vaqtida Related Data qayta-qayta ko‘rib chiqiladi va bu umumiy qidiruv xarajati O(N × M) bo‘lishiga olib kelishi mumkin.
Buni yaxshilash uchun Related Data’ni targetId asosida oldindan qayta ishlab, Lookup tuzilmasini yaratdim.
예시)
const relatedNameMap = new Map<string, string[]>();
relatedData.forEach( item => {
const names = relatedNameMap.get(item.targetId) ?? [];
names.push(item.name);
relatedNameMap.set(item.targetId, names);
});
6. Qayta ishlangan ma’lumotlarni Grid’ga uzatish
Related Data’ni oldindan qayta ishlagach, natijani Main Data bilan birlashtirib, Grid’ga uzatadigan tarzda amalga oshirishni o‘zgartirdim.
Avval zarur ma’lumotlar qidirilar va Cell yaratilganda qiymatlar birlashtirilardi.
Katak yaratish
↓
Bog‘liq ma’lumotlarni o‘rganish
↓
Ma’lumotlarni birlashtirish va o‘zgartirish
↓
Katakni render qilish
Ushbu yondashuvda Grid ma’lumotlari yangilanganda, saralanganda yoki filtrlanganda Cell qiymatlari qayta hisoblanishi mumkin edi.
Takomillashtirishdan so‘ng, avval yaratilgan relatedNameMap’dan foydalanib, Grid’ga uzatishdan oldin zarur qiymatlarni Main Data tarkibiga kiritdik.
예시)
const gridData = mainData.map( row => ({
...row,
relatedNames: relatedNameMap.get(row.id)?.join(“, ”) ?? “",
}));
const columnDefs = [ {
field: 'relatedNames',
headerName: 'Related Data',
},];
Bu har safar Cell yaratilganda takroriy ma’lumotlarni o‘rganish va qayta ishlash jarayonlarini render qilish bosqichidan olib tashlash imkonini berdi.
Xususan, ma’lumotlar yangilanganda, saralanganda yoki filtrlanganda ham Cell qiymatlarini shakllantirish uchun Related Data’ni qayta o‘rganish zarurati qolmadi. Grid ekranda allaqachon hisoblangan qiymatlarni ko‘rsatishga e’tibor qaratishi mumkin edi.
Natijada, bu o‘zgarishning ahamiyati faqat Cell’larni render qilish tezligini oshirishda emas, balki takroriy operatsiyalarni render qilish jarayonidan ajratish va hisoblash natijalaridan qayta foydalanishda bo‘ldi.
7. Faqat virtualizatsiya yordamida hal qilib bo‘lmaydigan muammolar
Grid’da o‘n minglab ma’lumot yozuvlarini ko‘rsatishda har bir Row’ni haqiqiy DOM elementi sifatida yaratish samarasizdir. Biz foydalangan AG Grid ham Virtualization orqali faqat joriy Viewport atrofida kerak bo‘ladigan Row’larni render qildi, shu sababli haqiqiy DOM’da yaratiladigan elementlar doirasi cheklangan edi.
Biroq Virtualization’ni qo‘llash katta hajmdagi ma’lumotlarni qayta ishlashda yuzaga keladigan barcha unumdorlik muammolarini hal qilmaydi. Virtualization, asosan, ekranda render qilinadigan DOM doirasini qisqartiradi; u Grid’ga uzatiladigan ma’lumotlarni shakllantirish yoki Cell’larda ko‘rsatiladigan qiymatlarni hisoblash bilan bog‘liq JavaScript operatsiyalarini kamaytirmaydi.
O‘sha paytda o‘n minglab ma’lumot yozuvlarini qayta ishlash turli manbalarga ajratilgan ma’lumotlarni o‘rganish va birlashtirishni talab qilardi. Shuning uchun takomillashtirishning asosiy yo‘nalishi Grid’ning render qilish doirasini qisqartirishdan ko‘ra, Grid ma’lumotlarini shakllantirish yoki Cell qiymatlarini hisoblashda takroran bajariladigan o‘rganish va qayta ishlash xarajatlarini kamaytirish edi.
8. Xulosa
Ushbu tajribaga Main Thread nuqtayi nazaridan nazar tashlasak, biz qo‘llagan takomillashtirishlar oxir-oqibat Main Thread bajarishi kerak bo‘lgan ish hajmini kamaytirishga qaratilgan edi.
|
Muammo |
Takomillashtirish yo‘nalishi |
|---|---|
|
Takroriy ma’lumotlarni o‘rganish |
Lookup tuzilmasini yaratish |
|
Cell darajasida ma’lumotlarni qayta ishlash |
Oldindan hisoblash |
|
Bir xil hisob-kitoblarni takrorlash |
Hisoblash natijalaridan qayta foydalanish |
Ushbu usullarni qo‘llaganingizdan keyin ham hisob-kitoblarning o‘zi og‘ir bo‘lsa, Task’ni bo‘lib bajarish yoki Web Worker’dan foydalanishni ham ko‘rib chiqishingiz mumkin.
Biroq boshidanoq murakkab yechimni qo‘llashdan ko‘ra, avvalo joriy Main Thread’da qaysi vazifalar takroran bajarilayotganini aniqlash muhim deb hisobladim.
Buning sababi shundaki, keraksiz o‘rganish va takroriy hisob-kitoblarni shunchaki bartaraf etishning o‘zi Main Thread qayta ishlashi kerak bo‘lgan umumiy ish hajmini kamaytirishi mumkin.
9. Xulosa
Ushbu tajriba orqali men katta hajmdagi ma’lumotlar bilan ishlashda nafaqat render qilish unumdorligini, balki ma’lumotlar ekranda ko‘rsatilishidan oldin qaysi operatsiyalar takrorlanayotganini ham tekshirish zarurligini angladim. Shuningdek, unumdorlikni optimallashtirish faqat muayyan funksiyaning bajarilish vaqtini qisqartirishdan iborat emasligini; eng avvalo, keraksiz hisob-kitoblar takrorlanmaydigan tarzda ma’lumotlarni qayta ishlash oqimini loyihalash muhimligini o‘rgandim.
Jung