Dipublikasikan: 2 Oktober 2024
Saat mulai menggunakan fitur CSS baru, penting untuk memahami dampaknya terhadap performa situs Anda, baik positif maupun negatif. Dengan @property
yang kini tersedia di Dasar Pengukuran, postingan ini akan membahas dampaknya terhadap performa, dan hal-hal yang dapat Anda lakukan untuk membantu mencegah dampak negatif.
Melakukan benchmark performa CSS dengan PerfTestRunner
Untuk membuat tolok ukur performa CSS, kami membuat rangkaian pengujian "CSS Selector Benchmark". Alat ini didukung oleh PerfTestRunner
Chromium dan menjalankan benchmark dampak performa CSS. PerfTestRunner
ini adalah yang digunakan Blink–mesin rendering dasar Chromium–untuk pengujian performa internalnya.
Runner menyertakan metode measureRunsPerSecond
yang digunakan untuk pengujian. Makin tinggi jumlah operasi per detik, makin baik. Benchmark measureRunsPerSecond
dasar dengan library ini terlihat seperti ini:
const testResults = PerfTestRunner.measureRunsPerSecond({
"Test Description",
iterationCount: 5,
bootstrap: function() {
// Code to execute before all iterations run
// For example, you can inject a style sheet here
},
setup: function() {
// Code to execute before a single iteration
},
run: function() {
// The actual test that gets run and measured.
// A typical test adjusts something on the page causing a style or layout invalidation
},
tearDown: function() {
// Code to execute after a single iteration has finished
// For example, undo DOM adjustments made within run()
},
done: function() {
// Code to be run after all iterations have finished.
// For example, remove the style sheets that were injected in the bootstrap phase
},
});
Setiap opsi untuk measureRunsPerSecond
dijelaskan melalui komentar dalam blok kode, dengan fungsi run
menjadi bagian inti yang diukur.
Benchmark Pemilih CSS memerlukan hierarki DOM
Karena kinerja pemilih CSS juga bergantung pada ukuran DOM, tolok ukur ini memerlukan hierarki DOM yang berukuran cukup besar. Hierarki DOM ini dibuat, bukan dibuat secara manual.
Misalnya, fungsi makeTree
berikut adalah bagian dari benchmark @property
. Kode ini membuat pohon berisi 1.000 elemen, setiap elemen dengan beberapa turunan yang disarangkan di dalamnya.
const $container = document.querySelector('#container');
function makeTree(parentEl, numSiblings) {
for (var i = 0; i <= numSiblings; i++) {
$container.appendChild(
createElement('div', {
className: `tagDiv wrap${i}`,
innerHTML: `<div class="tagDiv layer1" data-div="layer1">
<div class="tagDiv layer2">
<ul class="tagUl">
<li class="tagLi"><b class="tagB"><a href="/" class="tagA link" data-select="link">Select</a></b></li>
</ul>
</div>
</div>`,
})
);
}
}
makeTree($container, 1000);
Karena benchmark pemilih CSS tidak mengubah hierarki DOM, pembuatan hierarki ini hanya dieksekusi satu kali, sebelum benchmark apa pun berjalan.
Menjalankan benchmark
Untuk menjalankan benchmark yang merupakan bagian dari rangkaian pengujian, Anda harus memulai server web terlebih dahulu:
npm run start
Setelah dimulai, Anda dapat membuka benchmark di URL yang dipublikasikan dan menjalankan window.startTest()
secara manual.
Untuk menjalankan benchmark ini secara terpisah—tanpa intervensi atau ekstensi apa pun— Puppeteer dipicu dari CLI untuk memuat dan menjalankan benchmark yang diteruskan dalam tolok ukur.
Untuk benchmark @property
ini secara khusus, daripada membuka halaman yang relevan di URL-nya http://localhost:3000/benchmarks/at-rule/at-property.html
, panggil perintah berikut di CLI:
npm run benchmark at-rule/at-property
Tindakan ini akan memuat halaman melalui Puppeteer, otomatis memanggil window.startTest()
, dan melaporkan kembali hasilnya.
Membuat tolok ukur performa properti CSS
Untuk menjalankan benchmark performa properti CSS, Anda menjalankan benchmark seberapa cepat properti tersebut dapat menangani pembatalan validasi gaya dan tugas penghitungan ulang gaya berikutnya yang perlu dilakukan browser.
Pembuatan gaya tidak valid adalah proses menandai elemen mana yang gayanya perlu dihitung ulang sebagai respons terhadap perubahan di DOM. Pendekatan yang paling sederhana adalah dengan membatalkan semuanya sebagai respons terhadap setiap perubahan.
Saat melakukannya, ada perbedaan yang harus dibuat antara properti CSS yang diwarisi dan properti CSS yang tidak diwarisi.
- Ketika properti CSS yang mewarisi perubahan pada elemen yang ditargetkan, gaya kemungkinan semua elemen pada subhierarki di bawah elemen yang ditargetkan juga perlu diubah.
- Jika properti CSS yang tidak mewarisi perubahan pada elemen yang ditargetkan, hanya gaya untuk elemen individual tersebut yang akan dibatalkan validasinya.
Karena tidak akan adil untuk membandingkan properti yang mewarisi properti dengan properti yang tidak, ada dua set benchmark untuk dijalankan:
- Kumpulan benchmark dengan properti yang diwarisi.
- Kumpulan benchmark dengan properti yang tidak diwarisi.
Penting untuk memilih properti yang akan diukur dengan cermat. Meskipun beberapa properti (seperti accent-color
) hanya membatalkan gaya, ada banyak properti (seperti writing-mode
) yang juga membatalkan hal lain seperti tata letak atau cat. Anda menginginkan properti yang hanya membatalkan gaya.
Untuk menentukannya, cari di daftar properti CSS Blink. Setiap properti memiliki kolom invalidate
yang mencantumkan apa yang dibatalkan validasinya.
Selain itu, Anda juga harus memilih properti yang tidak ditandai sebagai independent
dari daftar tersebut, karena menjalankan benchmark untuk properti tersebut akan mendistorsi hasilnya. Properti independen tidak memiliki efek samping pada properti atau tanda lainnya. Jika hanya properti independen yang berubah, Blink akan menggunakan jalur kode cepat yang meng-clone gaya turunan dan memperbarui nilai baru dalam salinan yang di-clone tersebut. Pendekatan ini lebih cepat daripada melakukan penghitungan ulang penuh.
Tolok ukur performa properti CSS yang mewarisi
Kumpulan tolok ukur pertama berfokus pada properti CSS yang mewarisi. Ada tiga jenis properti yang diwarisi untuk diuji dan dibandingkan satu sama lain:
- Properti reguler yang mewarisi:
accent-color
. - Properti kustom yang tidak terdaftar:
--unregistered
. - Properti khusus yang didaftarkan dengan
inherits: true
:--registered
.
Properti kustom yang tidak terdaftar ditambahkan ke daftar ini karena properti tersebut diwarisi secara default.
Seperti yang disebutkan sebelumnya, properti yang mewarisi dipilih dengan cermat sehingga hanya membatalkan gaya dan properti yang tidak ditandai sebagai independent
.
Untuk properti kustom terdaftar, hanya properti dengan deskripsi inherits
yang ditetapkan ke benar yang diuji dalam operasi ini. Deskripsi inherits
menentukan apakah properti diturunkan ke turunan atau tidak. Tidak masalah apakah properti ini terdaftar melalui CSS @property
atau JavaScript CSS.registerProperty
, karena pendaftaran itu sendiri bukan bagian dari benchmark.
Tolok ukur
Seperti yang telah disebutkan, halaman yang berisi benchmark dimulai dengan membuat hierarki DOM sehingga halaman memiliki kumpulan node yang cukup besar untuk melihat dampak perubahan apa pun.
Setiap benchmark mengubah nilai properti, lalu memicu pembatalan validasi gaya. Benchmark ini pada dasarnya mengukur waktu yang diperlukan penghitungan ulang halaman berikutnya untuk mengevaluasi ulang semua gaya yang tidak valid tersebut.
Setelah benchmark tunggal selesai, semua gaya yang dimasukkan akan direset sehingga benchmark berikutnya dapat dimulai.
Misalnya, benchmark yang mengukur performa perubahan gaya --registered
terlihat seperti ini:
let i = 0;
PerfTestRunner.measureRunsPerSecond({
description,
iterationCount: 5,
bootstrap: () => {
setCSS(`@property --registered {
syntax: "<number>";
initial-value: 0;
inherits: true;
}`);
},
setup: function() {
// NO-OP
},
run: function() {
document.documentElement.style.setProperty('--registered', i);
window.getComputedStyle(document.documentElement).getPropertyValue('--registered'); // Force style recalculation
i = (i == 0) ? 1 : 0;
},
teardown: () => {
document.documentElement.style.removeProperty('--registered');
},
done: (results) => {
resetCSS();
resolve(results);
},
});
Benchmark yang menguji jenis properti lainnya berfungsi dengan cara yang sama, tetapi memiliki bootstrap
kosong karena tidak ada properti yang akan didaftarkan.
Hasil
Menjalankan benchmark ini dengan 20 iterasi di MacBook Pro 2021 (Apple M1 Pro) dengan RAM 16 GB memberikan rata-rata berikut:
- Properti reguler yang diwarisi (
accent-color
): 163 kali berjalan per detik (= 6,13 md per operasi) - Properti kustom yang tidak terdaftar (
--unregistered
): 256 kali berjalan per detik (= 3,90 md per operasi) - Properti kustom terdaftar dengan
inherits: true
(--registered
): 252 kali berjalan per detik (= 3,96 md per operasi)
Pada beberapa kali pengoperasian, benchmark menghasilkan hasil yang serupa.
Hasilnya menunjukkan bahwa mendaftarkan properti kustom memiliki biaya yang sangat kecil jika dibandingkan dengan tidak mendaftarkan properti kustom. Properti kustom terdaftar yang diwarisi berjalan dengan kecepatan 98% dari kecepatan properti kustom yang tidak terdaftar. Dalam angka absolut, mendaftarkan properti kustom akan menambahkan overhead 0,06 md.
Membuat tolok ukur performa properti CSS yang tidak diwarisi
Properti berikutnya yang akan diukur adalah properti yang tidak diwarisi. Di sini, hanya ada dua jenis properti yang dapat diukur:
- Properti reguler yang tidak diwarisi:
z-index
. - Properti kustom terdaftar dengan
inherits: false
:--registered-no-inherit
.
Properti kustom yang tidak terdaftar tidak dapat menjadi bagian dari tolok ukur ini karena properti tersebut selalu diwarisi.
Tolok ukur
Benchmark ini sangat mirip dengan skenario sebelumnya. Untuk pengujian dengan --registered-no-inherit
, pendaftaran properti berikut dimasukkan dalam fase bootstrap
benchmark:
@property --registered-no-inherit {
syntax: "<number>";
initial-value: 0;
inherits: false;
}
Hasil
Menjalankan tolok ukur ini dengan 20 iterasi pada MacBook Pro 2021 (Apple M1 Pro) dengan RAM 16GB memberikan rata-rata berikut:
- Properti reguler yang tidak mewarisi: 290.269 run per detik (= 3,44 μs per run)
- Properti Kustom Terdaftar yang tidak diwarisi: 214.110 dijalankan per detik (= 4,67 µd per operasi)
Pengujian diulang selama beberapa kali dan ini adalah hasil umumnya.
Hal yang menonjol di sini adalah properti yang tidak mewarisi bekerja jauh lebih cepat daripada properti yang mewarisi. Meskipun hal ini sudah diperkirakan untuk properti reguler, hal ini juga berlaku untuk properti kustom.
- Untuk properti reguler, jumlah pengoperasian meningkat dari 163 pengoperasian per detik menjadi lebih dari 290 ribu pengoperasian per detik, peningkatan performa sebesar 1.780%.
- Untuk properti kustom, jumlah pengoperasian meningkat dari 252 pengoperasian per detik menjadi lebih dari 214 ribu pengoperasian per detik, peningkatan performa sebesar 848%.
Hal penting yang perlu diingat adalah penggunaan inherits: false
saat mendaftarkan properti kustom memiliki dampak yang signifikan. Jika Anda dapat mendaftarkan properti khusus Anda ke inherits: false
, Anda harus melakukannya.
Tolok ukur bonus: beberapa pendaftaran properti kustom
Hal menarik lainnya untuk diukur adalah dampak dari memiliki banyak pendaftaran properti kustom. Untuk melakukannya, jalankan kembali pengujian dengan --registered-no-inherit
yang melakukan 25.000 pendaftaran properti kustom lainnya di awal. Properti kustom ini digunakan di :root
.
Pendaftaran ini dilakukan pada langkah setup
benchmark:
setup: () => {
const propertyRegistrations = [];
const declarations = [];
for (let i = 0; i < 25000; i++) {
propertyRegistrations.push(`@property --custom-${i} { syntax: "<number>"; initial-value: 0; inherits: true; }`);
declarations.push(`--custom-${i}: ${Math.random()}`);
}
setCSS(`${propertyRegistrations.join("\n")}
:root {
${declarations.join("\n")}
}`);
},
Pengoperasian per detik untuk benchmark ini sangat mirip dengan hasil untuk "Registered Custom Property that does not inherit" (214.110 pengoperasian per detik versus 213.158 pengoperasian per detik), tetapi itu bukan bagian yang menarik untuk dilihat. Bagaimanapun, perubahan pada satu properti kustom tidak akan terpengaruh oleh pendaftaran dari properti lain.
Bagian yang menarik dari pengujian ini adalah mengukur dampak dari pendaftaran itu sendiri. Beralih ke DevTools, Anda dapat melihat bahwa 25.000 pendaftaran properti kustom memiliki biaya penghitungan ulang gaya awal sedikit di atas 30ms
. Setelah selesai, keberadaan pendaftaran ini tidak akan memberikan dampak apa pun.
Kesimpulan dan poin-poin penting
Singkatnya, ada tiga hal yang dapat diambil dari semua ini:
Mendaftarkan properti kustom dengan
@property
akan sedikit mengurangi performa. Biaya ini sering kali dapat diabaikan karena dengan mendaftarkan properti kustom, Anda membuka potensi penuhnya yang tidak dapat dicapai tanpa melakukannya.Menggunakan
inherits: false
saat mendaftarkan properti kustom akan berdampak signifikan. Dengan begitu, Anda mencegah properti mewarisi. Oleh karena itu, saat nilai properti berubah, perubahan tersebut hanya memengaruhi gaya elemen yang cocok, bukan seluruh sub-pohon.Memiliki sedikit versus banyak pendaftaran
@property
tidak memengaruhi penghitungan ulang gaya. Hanya ada biaya awal yang sangat kecil saat melakukan pendaftaran, tetapi setelah itu Anda sudah siap.