من فضلك تسجيل الدخول أو تسجيل لتفعل ذلك.

أنا أكره كود قياس الأداء، تمامًا مثل أي إنسان (والذي، في هذه المرحلة، ربما لا يكون معظم مشاهدي هذا ¯_(ツ)_/¯). من الممتع أكثر التظاهر بأن التخزين المؤقت لقيمة ما أدى إلى زيادة الأداء بنسبة 1000% بدلاً من اختبارها لمعرفة ما فعلته. للأسف، لا يزال قياس الأداء في JavaScript ضروريًا، خاصة عند استخدام JavaScript (“https://byteofdev.com/posts/webassembly/”> متى لا ينبغي أن يكون؟) في التطبيقات الأكثر حساسية للأداء. لسوء الحظ، نظرًا للعديد من قراراتها المعمارية الأساسية، لا تجعل JavaScript عملية قياس الأداء أسهل.

ما هو الخطأ في جافا سكريبت؟”-1″ href=”http://byteofdev.com/#what-is-wrong-with-javascript?” aria-label=”Link to What is wrong with JavaScript? section”>

يقوم مترجم JIT بتقليل الدقة (؟)”-1″ href=”http://byteofdev.com/#the-jit-compiler-decreases-accuracy(?)” aria-label=”Link to The JIT compiler decreases accuracy(?) section”>

بالنسبة لأولئك الذين ليسوا على دراية بسحر لغات البرمجة النصية الحديثة مثل JavaScript، يمكن أن تكون هندستها المعمارية معقدة جدًا. بدلاً من تشغيل التعليمات البرمجية فقط من خلال مترجم ينشر التعليمات على الفور، تستخدم معظم محركات JavaScript بنية أكثر تشابهًا مع لغة مجمعة مثل لغة C، فهي تدمج”https://v8.dev/blog/turbofan-jit”> طبقات متعددة من “المترجمين”.

يقدم كل من هذه المترجمات مقايضة مختلفة بين وقت الترجمة وأداء وقت التشغيل، لذلك لا يحتاج المستخدم إلى إنفاق حساب تحسين التعليمات البرمجية التي نادرًا ما يتم تشغيلها مع الاستفادة من مزايا أداء المترجم الأكثر تقدمًا للتعليمات البرمجية التي يتم تشغيلها في أغلب الأحيان ( “المسارات الساخنة”). هناك أيضًا بعض التعقيدات الأخرى التي تنشأ عند استخدام المترجمات المحسنة التي تتضمن كلمات برمجة فاخرة مثل “”https://mrale.ph/blog/2015/01/11/whats-up-with-monomorphism.html”> أحادية الوظيفة”، لكنني سأعفيك وأتجنب الحديث عن ذلك هنا.

إذًا… لماذا يهم هذا الأمر بالنسبة لقياس الأداء؟ حسنًا، كما كنت قد خمنت، لأن القياس هو قياس أداء من التعليمات البرمجية، يمكن أن يكون لمترجم JIT تأثير كبير جدًا. غالبًا ما تشهد الأجزاء الصغيرة من التعليمات البرمجية، عند قياسها، تحسينات في الأداء بمعدل 10x+ بعد التحسين الكامل، مما يؤدي إلى حدوث الكثير من الأخطاء في النتائج. على سبيل المثال، في إعداد قياس الأداء الأساسي لديك (لا تستخدم أي شيء مثل ما يلي لأسباب متعددة):

for (int i = 0; i<1000; i++) {    console.time()    // do some expensive work    console.timeEnd()}

(لا تقلق، سنتحدث عن ذلك console.time أيضاً)

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

لذلك، حتى داخل محرك واحد، هناك 2-4 طرق مختلفة لتشغيل التعليمات البرمجية الخاصة بك بمستويات مختلفة من الأداء. أوه، أيضًا، من الصعب للغاية في بعض الحالات ضمان تمكين مستويات تحسين معينة. استمتع :).

تبذل المحركات قصارى جهدها لمنعك من التوقيت بدقة”-1″ href=”http://byteofdev.com/#engines-do-their-best-to-stop-you-from-timing-accurately” aria-label=”Link to Engines do their best to stop you from timing accurately section”>

هل تعرف بصمات الأصابع؟ التقنية التي سمحت”https://getinsights.io/blog/posts/fingerprinting-do-not-track”> لا تتبع ليتم استخدامها ل يساعد تتبع؟ نعم، تبذل محركات JavaScript قصارى جهدها للتخفيف من هذه المشكلة. هذا الجهد، إلى جانب التحرك لمنع”https://xsleaks.dev/docs/attacks/timing-attacks/execution-timing/”> توقيت الهجمات، أدى إلى قيام محركات JavaScript بجعل التوقيت غير دقيق عن عمد، لذلك لا يستطيع المتسللون الحصول على قياسات دقيقة لأداء أجهزة الكمبيوتر الحالية أو مدى تكلفة عملية معينة. لسوء الحظ، هذا يعني أنه بدون تعديل الأشياء، فإن المعايير لديها نفس المشكلة.

لن يكون المثال الموجود في القسم السابق دقيقًا، لأنه يقاس بالمللي ثانية فقط. الآن، قم بإيقاف ذلك لـ performance.now(). عظيم، الآن لدينا الطوابع الزمنية بالميكروثانية!

// Badconsole.time();// workconsole.timeEnd();// Better?const t = performance.now();// workconsole.log(performance.now() - t);

باستثناء… أنها كلها بزيادات قدرها 100μs. الآن دعونا نجعل”https://developer.mozilla.org/en-US/docs/Web/API/Performance_API/High_precision_timing#reduced_precision”> أضف بعض الرؤوس للتخفيف من مخاطر توقيت الهجمات. عفوًا، لا يزال بإمكاننا زيادات بمقدار 5μs فقط. من المحتمل أن تكون دقة 5μs كافية للعديد من حالات الاستخدام، ولكن سيتعين عليك البحث في مكان آخر عن أي شيء يتطلب المزيد من التفاصيل. بقدر ما أعرف، لا يوجد متصفح يسمح بمزيد من الموقتات الدقيقة. Node.js يفعل ذلك، لكن بالطبع، هذا له مشاكله الخاصة.

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

كل بيئة مختلفة”-1″ href=”http://byteofdev.com/#every-environment-is-different” aria-label=”Link to Every environment is different section”>

أنا أحب”https://byteofdev.com/posts/what-is-bun/”> كعكة لما فعلته لدفع JavaScript من جانب الخادم إلى الأمام، ولكن يا إلهي، فإنه يجعل قياس أداء JavaScript للخوادم أكثر صعوبة. قبل بضع سنوات، كانت بيئات JavaScript الوحيدة التي يهتم بها الأشخاص من جانب الخادم هي Node.js و”https://byteofdev.com/posts/deno/”> دينووكلاهما يستخدم محرك V8 JavaScript (نفس المحرك في Chrome). يستخدم Bun بدلاً من ذلك JavaScriptCore، المحرك الموجود في Safari، والذي يتميز بخصائص أداء مختلفة تمامًا.

تعد مشكلة بيئات JavaScript المتعددة مع خصائص الأداء الخاصة بها جديدة نسبيًا في JavaScript من جانب الخادم ولكنها ابتليت بها العملاء لفترة طويلة. يمكن لمحركات JavaScript الثلاثة المختلفة شائعة الاستخدام، V8، وJSC، وSpiderMonkey لمتصفح Chrome، وSafari، وFirefox، على التوالي، أن تعمل جميعها بشكل أسرع أو أبطأ بشكل ملحوظ على جزء مماثل من التعليمات البرمجية.

أحد الأمثلة على هذه الاختلافات موجود في Tail Call Optimization (TCO). تعمل التكلفة الإجمالية للملكية (TCO) على تحسين الوظائف التي تتكرر في نهاية الجسم، مثل هذا:

function factorial(i, num = 1) {if (i == 1) return num;num *= i;i--;return factorial(i, num);}

حاول قياس الأداء factorial(100000) في كعكة. الآن، جرب نفس الشيء في Node.js أو Deno. يجب أن تحصل على خطأ مشابه لهذا:

function factorial(i, num=1) { ^RangeError: Maximum call stack size exceeded

في V8 (وبالامتداد Node.js وDeno) في كل مرة يتم فيها factorial() باستدعاء نفسه في النهاية، يقوم المحرك بإنشاء سياق وظيفي جديد تمامًا لتشغيل الوظيفة المتداخلة، والذي يقتصر في النهاية على مكدس الاستدعاءات. لكن لماذا لا يحدث هذا في بون؟ يقوم JavaScriptCore، الذي يستخدمه Bun، بتنفيذ التكلفة الإجمالية للملكية (TCO)، والتي تعمل على تحسين هذه الأنواع من الوظائف عن طريق تحويلها إلى حلقة for مثل هذا:

function factorial(i, num = 1) {while (i != 1) {num *= i;i--;}return i;}

لا يتجنب التصميم أعلاه حدود مكدس الاستدعاءات فحسب، بل إنه أيضًا أسرع بكثير لأنه لا يتطلب أي سياقات وظيفية جديدة، مما يعني أن وظائف مثل ما ورد أعلاه سيتم قياسها بشكل مختلف تمامًا تحت محركات مختلفة.

في الأساس، تعني هذه الاختلافات أنه يجب عليك قياس الأداء عبر جميع المحركات التي تتوقع تشغيل التعليمات البرمجية الخاصة بك للتأكد من أن التعليمات البرمجية السريعة في أحدها ليست بطيئة في محرك آخر. أيضًا، إذا كنت تقوم بتطوير مكتبة تتوقع استخدامها عبر العديد من الأنظمة الأساسية، فتأكد من تضمين المزيد من المحركات المتخصصة مثل”https://github.com/facebook/hermes”> هيرميس; لديهم خصائص أداء مختلفة بشكل جذري.

إشارات مشرفة”-1″ href=”http://byteofdev.com/#honorable-mentions” aria-label=”Link to Honorable mentions section”>

  • جامع القمامة وميله إلى إيقاف كل شيء مؤقتًا بشكل عشوائي
  • قدرة مترجم JIT على حذف كافة التعليمات البرمجية الخاصة بك لأنها “ليست ضرورية”
  • رسوم بيانية واسعة للغاية في معظم أدوات تطوير جافا سكريبت
  • أعتقد أنك حصلت على هذه النقطة

إذًا… ما الحل؟”-1″ href=”http://byteofdev.com/#so…-what-is-the-solution?” aria-label=”Link to So… what is the solution? section”>

أتمنى أن أشير إلى حزمة npm التي تحل كل هذه المشكلات، لكن لا توجد حزمة واحدة حقًا.

على الخادم، لديك وقت أسهل قليلا. يمكنك استخدام”https://v8.dev/docs/d8″> د8 للتحكم يدويًا في مستويات التحسين، والتحكم في أداة تجميع البيانات المهملة، والحصول على توقيت دقيق. بالطبع، ستحتاج إلى بعض Bash-fu لإعداد مسار مرجعي مصمم جيدًا لهذا الغرض، نظرًا لأن d8 للأسف ليس مدمجًا جيدًا (أو متكاملًا على الإطلاق) مع Node.js. يمكنك أيضًا تمكين علامات معينة في Node.js للحصول على نتائج مماثلة، ولكنك ستفوتك ميزات مثل تمكين مستويات تحسين محددة.

v8 --sparkplug --always-sparkplug --no-opt [file]

مثال على D8 مع تمكين طبقة تجميع محددة (sparkplug). يتضمن D8، بشكل افتراضي، المزيد من التحكم في GC والمزيد من معلومات التصحيح بشكل عام.

يمكنك الحصول على بعض الميزات المماثلة على JavaScriptCore؟؟؟ بصراحة، لم أستخدم واجهة سطر الأوامر الخاصة بـ JavaScriptCore كثيرًا، وهي كذلك بالفعل بشدة موثقة. يمكنك تمكين مستويات محددة باستخدام”https://trac.webkit.org/wiki/JSC”> أعلام سطر الأوامر الخاصة بهم، لكنني لست متأكدًا من مقدار معلومات التصحيح التي يمكنك استردادها. تتضمن الكعكة أيضًا بعض العناصر المفيدة”https://bun.sh/docs/project/benchmarking”> أدوات قياس الأداء، لكنها محدودة بشكل مشابه لـ Node.js.

لسوء الحظ، كل هذا يتطلب المحرك الأساسي/الإصدار التجريبي للمحرك، والذي قد يكون من الصعب جدًا الحصول عليه. لقد وجدت أن أبسط طريقة لإدارة المحركات هي”https://github.com/devsnek/esvu”>esvu مقترنًا بـ”https://github.com/bterlson/eshost-cli”>eshost-cli، حيث أنهما معًا يجعلان إدارة المحركات وتشغيل التعليمات البرمجية عبرها أسهل إلى حد كبير. بالطبع، لا يزال هناك الكثير من العمل اليدوي المطلوب، حيث تقوم هذه الأدوات فقط بإدارة التعليمات البرمجية قيد التشغيل عبر محركات مختلفة – لا يزال يتعين عليك كتابة كود القياس بنفسك.

إذا كنت تحاول فقط قياس الأداء على المحرك باستخدام الخيارات الافتراضية بأكبر قدر ممكن من الدقة على الخادم، فهناك أدوات Node.js الجاهزة مثل”https://github.com/evanwashere/mitata”>ميتاتا التي تساعد على تحسين دقة التوقيت والأخطاء المتعلقة بـ GC. يمكن أيضًا استخدام العديد من هذه الأدوات، مثل Mitata، عبر العديد من المحركات؛ وبطبيعة الحال، لا يزال يتعين عليك إعداد خط أنابيب مثل ما ورد أعلاه.

على المتصفح، كل شيء أكثر صعوبة بكثير. لا أعرف أي حلول لتوقيت أكثر دقة، كما أن التحكم في المحرك محدود للغاية. معظم المعلومات التي يمكنك الحصول عليها فيما يتعلق بأداء JavaScript في وقت التشغيل في المتصفح ستكون من”https://developer.chrome.com/docs/devtools/performance”> أدوات تطوير الكروم، والتي توفر أدوات مساعدة لمحاكاة تباطؤ وحدة المعالجة المركزية للرسم البياني للهب الأساسي.

الاستنتاج”-1″ href=”http://byteofdev.com/#conclusion” aria-label=”Link to Conclusion section”>

العديد من قرارات التصميم نفسها التي جعلت JavaScript عالية الأداء (نسبيًا) ومحمولة تجعل قياس الأداء أكثر صعوبة بكثير مما هو عليه في اللغات الأخرى. هناك العديد من الأهداف التي يجب قياسها، ولديك تحكم أقل بكثير في كل هدف.

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

مهما اخترت قياس الأداء، آمل أن توضح لك هذه المقالة بعض المشكلات الموجودة في قياس أداء JavaScript. اسمحوا لي أن أعرف ما إذا كان البرنامج التعليمي حول تنفيذ بعض الأشياء التي وصفتها أعلاه سيكون مفيدًا.

اقرأ المزيد

ماركوس فريمان يرفض تقويض المدفعية عديمة الخبرة ستوكتون بينما يؤدي غياب كارسون بيك إلى تغيير الأضواء
اختراق المتجر الرسمي لوكالة الفضاء الأوروبية لسرقة بطاقات الدفع

Reactions

0
0
0
0
0
0
بالفعل كان رد فعل لهذا المنصب.

ردود الفعل