قياس أداء CSS @property

تاريخ النشر: 2 تشرين الأول (أكتوبر) 2024

عند بدء استخدام ميزة جديدة في CSS، من المهم فهم تأثيرها في أداء مواقعك الإلكترونية، سواء كان ذلك تأثيرًا إيجابيًا أو سلبيًا. بعد أن تمت إضافة "@property" الآن إلى Baseline، يمكنك الاطّلاع على هذه المشاركة لمعرفة تأثيرها في الأداء، والإجراءات التي يمكنك اتّخاذها لتجنُّب التأثير السلبي.

قياس أداء خدمة مقارنة الأسعار باستخدام PerfTestRunner

لقياس أداء خدمة CSS، أنشأنا مجموعة الاختبار "أداة قياس أداء أداة اختيار لغة CSS". ويستند إلى PerfTestRunner في Chromium، ويحدِّد معايير الأداء لـ CSS. هذه هي PerfTestRunner التي يستخدمها Blink، وهو محرّك العرض الأساسي في Chromium، لإجراء اختبارات الأداء الداخلية.

يتضمّن أداة التشغيل طريقة measureRunsPerSecond التي يتم استخدامها في الاختبارات. وكلما زاد عدد عمليات التنفيذ في الثانية، كان ذلك أفضل. يظهر مقياس الأداء الأساسي لـ measureRunsPerSecond باستخدام هذه المكتبة على النحو التالي:

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
  },
});

يتم وصف كل خيار من خيارات measureRunsPerSecond من خلال التعليقات في كتلة التعليمات البرمجية، وتكون دالة run هي الجزء الأساسي الذي يتم قياسه.

تتطلب مقاييس أداء أداة اختيار لغة CSS شجرة نموذج العناصر في المستند (DOM)

وبما أنّ أداء أدوات اختيار لغة CSS يعتمد أيضًا على حجم نموذج DOM، تحتاج هذه المقاييس إلى شجرة نموذج DOM ذات حجم مناسب. بدلاً من إنشاء شجرة DOM هذه يدويًا، يتم إنشاؤها.

على سبيل المثال، تكون الدالة makeTree التالية جزءًا من مقاييس الأداء @property. وهي تُنشئ شجرة من 1,000 عنصر، وكل عنصر يتضمّن بعض العناصر الفرعية المضمّنة.

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);

وبما أنّ مقاييس أداء أدوات اختيار CSS لا تعدّل شجرة DOM، لا يتم تنفيذ عملية إنشاء هذه الشجرة إلا مرة واحدة قبل تنفيذ أيّ من مقاييس الأداء.

إجراء قياس أداء

لتنفيذ اختبار أداء يُعدّ جزءًا من مجموعة الاختبار، عليك أولاً تشغيل خادم ويب:

npm run start

بعد بدء الاختبار، يمكنك الانتقال إلى المرجع على عنوان URL المنشور وتنفيذ window.startTest() يدويًا.

لتنفيذ هذه المقاييس بشكل منفصل، أي بدون أي إضافات أو عوامل أخرى، يتم تشغيل Puppeteer من سطر الأوامر لتحميل المقياس الذي تم تمريره وتنفيذه.

بالنسبة إلى مقاييس الأداء الخاصة بـ @property على وجه التحديد، بدلاً من الانتقال إلى الصفحة ذات الصلة على عنوان URL الخاص بها http://localhost:3000/benchmarks/at-rule/at-property.html، يتم استدعاء الأوامر التالية في واجهة سطر الأوامر:

npm run benchmark at-rule/at-property

يؤدي ذلك إلى تحميل الصفحة من خلال Puppeteer، واستدعاء window.startTest() تلقائيًا، وعرض النتائج.

قياس أداء خصائص CSS

لقياس أداء خاصيّة CSS، يمكنك قياس مدى سرعة معالجة إبطال الأنماط ومهمة إعادة احتساب الأنماط اللاحقة التي يحتاج المتصفح إلى تنفيذها.

إنّ إلغاء صلاحية الأسلوب هو عملية وضع علامة على العناصر التي تحتاج إلى إعادة احتساب أسلوبها استجابةً لتغيير في نموذج DOM. إنّ أبسط طريقة ممكنة هي إلغاء كل البيانات استجابةً لكل تغيير.

عند إجراء ذلك، يجب التمييز بين سمات CSS التي يتم اكتسابها وسماتها التي لا يتم اكتسابها.

  • عندما تتغيّر سمة CSS مكتسَبة في عنصر مستهدف، يجب أيضًا تغيير أنماط جميع العناصر المحتملة في الشجرة الفرعية تحت العنصر المستهدف.
  • عندما تتغيّر خاصية CSS لا ترث التغييرات على عنصر مستهدف، لا يتم إلغاء سوى أنماط هذا العنصر الفردي.

ولأنّه لن يكون من العدل مقارنة المواقع التي ترث السمات بالمواقع التي لا ترثها، هناك مجموعتَان من مقاييس الأداء المطلوب تنفيذها:

  • مجموعة من مقاييس الأداء التي تتضمّن خصائص مكتسَبة
  • مجموعة من مقاييس الأداء التي تحتوي على خصائص لا يتم اكتسابها

من المهم اختيار المواقع التي تريد قياس أدائها بعناية. على الرغم من أنّ بعض السمات (مثل accent-color) تعمل على إيقاف الأنماط فقط، هناك العديد من السمات (مثل writing-mode) التي تلغي أيضًا عناصر أخرى، مثل التنسيق أو الطلاء. تريد الخصائص التي تبطل الأنماط فقط.

لتحديد ذلك، ابحث عن العناصر في قائمة سمات CSS في Blink. يحتوي كلّ موقع على حقل invalidate يسرد ما يتمّ إلغاء صلاحيته.

بالإضافة إلى ذلك، من المهم أيضًا اختيار موقع إلكتروني غير مصنّف على أنّه independent من هذه القائمة، لأنّ قياس أداء موقع إلكتروني كهذا سيؤدي إلى تشويه النتائج. لا تُحدث المواقع المستقلة أيّ تأثيرات جانبية على المواقع أو الإشارات الأخرى. عندما تتغيّر السمات المستقلة فقط، يستخدم Blink مسار رمز سريعًا ينسخ نمط العنصر الفرعي ويُعدّل القيم الجديدة في النسخة التي تم نسخها. وهذا الأسلوب أسرع من إجراء عملية إعادة احتساب كاملة.

قياس أداء خصائص CSS التي يتم اكتسابها

تركّز المجموعة الأولى من مقاييس الأداء على خصائص CSS المُكتسَبة. هناك ثلاثة أنواع من المواقع التي يتم اختبارها ومقارنتها ببعضها:

  • موقع عادي يكتسب: accent-color.
  • خاصيّة مخصّصة غير مسجّلة: --unregistered
  • خاصيّة مخصّصة مسجّلة باستخدام inherits: true: --registered

تتم إضافة المواقع المخصّصة غير المسجّلة إلى هذه القائمة لأنّها ترثها تلقائيًا.

كما ذكرنا سابقًا، تم اختيار السمة التي يتم اكتسابها بعناية بحيث تكون سمة تلغي الأنماط فقط ولا يتم وضع علامة عليها على أنّها independent.

بالنسبة إلى السمات المخصّصة المسجَّلة، لا يتم اختبار سوى المواقع التي تتضمّن الواصف inherits على "صحيح" في عملية التشغيل هذه. يحدِّد وصف inherits ما إذا كان الموقع الإلكتروني يكتسب الإعدادات من المواقع الإلكترونية الفرعية أم لا. لا يهمّ ما إذا كان هذا الموقع مسجَّلاً من خلال CSS @property أو JavaScript CSS.registerProperty، لأنّ عملية التسجيل نفسها ليست جزءًا من مقياس الأداء.

مقاييس الأداء

كما سبق وذكرنا، تبدأ الصفحة التي تحتوي على مقاييس الأداء بإنشاء شجرة نموذج عناصر في المستند (DOM) حتى تتضمّن الصفحة مجموعة كبيرة بما يكفي من العقد لمعرفة أي تأثير للتغييرات.

يغيّر كل معيار قيمة خاصية ما، ما يؤدي إلى إلغاء صلاحية النمط. يقيس مقياس الأداء بشكل أساسي المدّة التي تستغرقها إعادة احتساب الصفحة التالية لإعادة تقييم كل هذه الأنماط غير الصالحة.

بعد الانتهاء من اختبار أداء واحد، تتم إعادة ضبط أي أنماط تم إدراجها لكي يبدأ اختبار الأداء التالي.

على سبيل المثال، يبدو مقياس الأداء المعياري الذي يقيس أداء تغيير أسلوب --registered على النحو التالي:

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);
  },
});

تعمل مقاييس الأداء التي تختبر الأنواع الأخرى من المواقع بالطريقة نفسها، ولكنّها تحتوي على bootstrap فارغ لأنّه لا يتوفّر موقع لتسجيله.

النتائج

عند إجراء 20 جولة من اختبارات الأداء هذه على جهاز MacBook Pro (Apple M1 Pro) لعام 2021 مع ذاكرة وصول عشوائي بسعة 16 غيغابايت، تحصل على المتوسطات التالية:

  • الموقع العادي الذي يكتسب (accent-color): 163 عملية تنفيذ في الثانية (= 6.13 ملي ثانية لكل عملية تنفيذ)
  • الموقع المخصّص غير المسجَّل (--unregistered): 256 عملية تنفيذ في الثانية (= 3.90 ملي ثانية لكل عملية تنفيذ)
  • الموقع المخصّص المسجّل مع inherits: true (--registered): 252 عملية تنفيذ في الثانية (= 3.96 ملي ثانية لكل عملية تنفيذ)

في عمليات تشغيل متعددة، تحقِّق مقاييس الأداء نتائج مشابهة.

رسم بياني شريطي يعرض نتائج المواقع التي تكتسِب السمات تحقّق الأرقام الأعلى أداءً أسرع.
الشكل: رسم بياني شريطي يعرض نتائج السمات التي تكتسِب القيم ويكون الأداء أسرع مع الأرقام الأعلى.

تُظهر النتائج أنّ تسجيل موقع مخصّص يتطلّب تكلفة بسيطة جدًا مقارنةً بعدم تسجيل الموقع المخصّص. إنّ الخصائص المخصّصة المسجّلة التي تكتسب السمات تعمل بسرعة %98 من سرعة الخصائص المخصّصة غير المسجّلة. في الأرقام المطلقة، يؤدي تسجيل الموقع المخصّص إلى زيادة زمن الاستجابة بمقدار 0.06 ملي ثانية.

قياس أداء خصائص CSS التي لا يتم اكتسابها

الخصائص التالية التي يجب قياس أدائها هي تلك التي لا يتم اكتسابها. في ما يلي نوعان فقط من المواقع التي يمكن قياس أدائها:

  • موقع عادي لا يتم اكتسابه: z-index.
  • خاصيّة مخصّصة مسجّلة باستخدام inherits: false: --registered-no-inherit

لا يمكن أن تكون المواقع المخصّصة غير المسجّلة جزءًا من هذا المقياس لأنّ هذه المواقع تكتسِب السمات دائمًا.

مقاييس الأداء

وتشبه مقاييس الأداء إلى حد كبير السيناريوهات السابقة. بالنسبة إلى الاختبار الذي يتضمّن --registered-no-inherit، يتمّ إدراج تسجيل الموقع التالي في المرحلة bootstrap من مقياس الأداء:

@property --registered-no-inherit {
  syntax: "<number>";
  initial-value: 0;
  inherits: false;
}

النتائج

عند إجراء 20 جولة من اختبارات الأداء هذه على جهاز MacBook Pro (Apple M1 Pro) لعام 2021 مع ذاكرة وصول عشوائي بسعة 16 غيغابايت، تحصل على المتوسطات التالية:

  • السمة العادية التي لا تكتسِب القيمة من السمات الأخرى: 290,269 عملية تشغيل في الثانية (= 3.44µs لكل عملية تشغيل)
  • سمة مخصّصة مسجّلة لا تكتسِب القيم: 214,110 عملية تنفيذ في الثانية (= 4.67µs لكل عملية تنفيذ)

تم تكرار الاختبار على مدار عدّة عمليات تشغيل وكانت هذه هي النتائج المعتادة.

رسم بياني شريطي يعرض نتائج المواقع التي لا ترث تحقّق الأرقام الأعلى أداءً أسرع.
الشكل: رسم بياني شريطي يعرض نتائج السمات التي لا ترث ويكون الأداء أسرع مع الأرقام الأعلى.

يُرجى العِلم أنّ الخصائص التي لا ترث الأداء تكون أسرع بكثير من الخصائص التي ترث الأداء. على الرغم من أنّ هذا الأمر كان متوقّعًا للمواقع العادية، إلا أنّه ينطبق أيضًا على المواقع المخصّصة.

  • بالنسبة إلى المواقع العادية، ارتفع عدد عمليات التنفيذ من 163 عملية تنفيذ في الثانية إلى أكثر من 290 ألف عملية تنفيذ في الثانية، ما يمثّل زيادة في الأداء بنسبة ‎1780%.
  • بالنسبة إلى المواقع المخصّصة، ارتفع عدد عمليات التنفيذ من 252 عملية تنفيذ في الثانية إلى أكثر من 214 ألف عملية تنفيذ في الثانية، ما يمثّل زيادة في الأداء بنسبة %848.

والخلاصة الأساسية من ذلك هي أنّ استخدام inherits: false عند تسجيل خاصية مخصّصة له تأثير فعّال. إذا كان بإمكانك تسجيل موقعك المخصّص باستخدام inherits: false، ننصحك بالتأكيد بإجراء ذلك.

مقياس الأداء الإضافي: عمليات تسجيل متعدّدة لمواقع مخصّصة

هناك عامل آخر مثير للاهتمام يجب قياس أدائه، وهو تأثير توفّر الكثير من عمليات تسجيل المواقع المخصّصة. لإجراء ذلك، أعِد إجراء الاختبار باستخدام --registered-no-inherit مع إجراء 25,000 عملية تسجيل أخرى للموقع المخصّص في البداية. يتم استخدام هذه السمات المخصّصة في :root.

يتم إجراء عمليات التسجيل هذه في الخطوة setup من مقياس الأداء:

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")}
  }`);
},

إنّ عدد عمليات التنفيذ في الثانية لهذا المقياس مشابه جدًا لنتيجة "الخاصية المخصّصة المسجّلة التي لا تكتسِب الخصائص" (214,110 عملية تنفيذ في الثانية مقابل 213,158 عملية تنفيذ في الثانية)، ولكنّ هذا ليس هو الجزء المثير للاهتمام الذي يجب الاطّلاع عليه. بعد كل شيء، من المتوقّع ألا يتأثّر تغيير خاصية مخصّصة واحدة بعمليات التسجيل من المواقع الأخرى.

الجزء المثير للاهتمام في هذا الاختبار هو قياس تأثير عمليات التسجيل نفسها. عند الانتقال إلى DevTools، يمكنك ملاحظة أنّ تكلفة إعادة احتساب الأنماط الأولية لـ 25,000 عملية تسجيل مواقع مخصّصة تزيد قليلاً عن 30ms. بعد الانتهاء من ذلك، لن يكون لوجود عمليات التسجيل هذه أي تأثير آخر.

لقطة شاشة أدوات المطوّر مع تكلفة &quot;إعادة احتساب التصميم&quot; لإجراء 25 ألف عملية تسجيل مواقع مخصّصة مميّزة يشير التلميح إلى أنّ الأمر استغرق 32.42 ملّي ثانية.
الشكل: لقطة شاشة لأدوات مطوّري البرامج مع تكلفة "إعادة احتساب التصميم" لإجراء 25 ألف عملية تسجيل مواقع مخصّصة مميّزة يشير تلميح الأداة إلى أنّه استغرق 32.42ms

الخاتمة والنقاط الرئيسية

في ما يلي ثلاث نقاط يمكن استخلاصها من كلّ ما سبق:

  • يؤدي تسجيل موقع مخصّص في @property إلى تحقيق تكلفة بسيطة في الأداء. غالبًا ما تكون هذه التكلفة غير ملحوظة لأنّ تسجيل المواقع المخصّصة يساعدك في الاستفادة من إمكاناتها الكاملة، وهو أمر لا يمكن تحقيقه بدون إجراء ذلك.

  • إنّ استخدام inherits: false عند تسجيل موقع مخصّص له تأثير ملحوظ. باستخدام هذه الطريقة، سيتم منع اكتساب الموقع. عند تغيير قيمة السمة، سيؤثّر ذلك فقط في أنماط العنصر المطابق بدلاً من الشجرة الفرعية بأكملها.

  • لا يؤثّر عدد عمليات تسجيل @property القليل مقارنةً بعددها الكبير في إعادة احتساب الأنماط. لا تُفرض سوى تكلفة زهيدة جدًا في البداية عند إجراء عمليات التسجيل، ولكن بعد الانتهاء منها، لن تضطر إلى دفع أي رسوم أخرى.