مقدمة: لماذا يُعد تحليل الأداء الخطوة الأولى نحو التحسين
\nتحسين الكود دون تحليل مسبق للأداء هو بمثابة إطلاق نار في الظلام. غالبًا ما يقضي المطورون ساعات في "تحسين" أجزاء من الكود ليست في الحقيقة نقاط اختناق. يتيح لك تحليل أداء كود JavaScript تحديد الوظائف التي تستهلك معظم وقت المعالج بدقة، وأين يحدث تسرب للذاكرة، وأي العمليات تحظر Event Loop.
\nفي هذه المقالة، سنستعرض الطرق العملية لتحليل الأداء في بيئة Node.js، باستخدام الأدوات المدمجة وأدوات المتصفح. ستتعلم كيفية إيجاد مشاكل الأداء الحقيقية، بدلاً من التخمين.
\n\n1. أدوات تحليل أداء Node.js
\nتوفر Node.js العديد من الأدوات القوية، ولكن غالبًا ما يتم التقليل من قيمتها. دعنا نستعرض ثلاثة أساليب رئيسية.
\n\n1.1. ملف تعريف V8 المدمج
\nأسهل طريقة للحصول على ملف تعريف هي تشغيل السكريبت مع العلم --prof:
node --prof app.js\nبعد انتهاء السكريبت، يتم إنشاء ملف isolate-0x...-v8.log. لتحويله إلى نص قابل للقراءة، استخدم:
node --prof-process isolate-0x...-v8.log > profile.txt\nمثال على الإخراج (مبسط):
\nStatistical profiling result from isolate-0x3a00000-v8.log, (1002 ticks, 1 unaccounted, 0 excluded).\n\n [Shared libraries]:\n ticks total nonlib name\n 10 1.0% /lib/x86_64-linux-gnu/libc-2.31.so\n\n [JavaScript]:\n ticks total nonlib name\n 450 44.9% 45.5% LazyCompile: *processData /home/user/app.js:12:17\n 200 20.0% 20.2% LazyCompile: *fetchFromDB /home/user/app.js:34:23\n 100 10.0% 10.1% Builtin: CallFunctionReceiver\n\nهنا نرى أن الدالة processData تستهلك ما يقرب من نصف الوقت - وهذا هدف واضح للتحسين.
1.2. Chrome DevTools لـ Node.js
\nللحصول على تحليل بصري أكثر، استخدم Chrome DevTools. قم بتشغيل التطبيق مع العلم --inspect:
node --inspect app.js\nافتح في Chrome chrome://inspect واضغط على "Open dedicated DevTools for Node". سترى علامات التبويب Profiler (أو Performance في الإصدارات الأحدث). سجل ملف تعريف، وقم بتنفيذ الإجراء المطلوب في التطبيق، ثم حلل Flame Graph (مخطط اللهب).
يظهر Flame Graph الوظائف التي تستدعي بعضها البعض ومقدار الوقت الذي تستغرقه. "الأعمدة" العريضة هي الوظائف الأكثر استهلاكًا للوقت.
\n\n1.3. console.time و console.timeEnd
\nللقياسات السريعة والنقطية، استخدم الزوج console.time / console.timeEnd:
console.time('parseJSON');\nconst data = JSON.parse(largeString);\nconsole.timeEnd('parseJSON'); // parseJSON: 12.345 ms\nهذه الطريقة لا تتطلب تشغيل ملف تعريف منفصل وهي مثالية لتصحيح الأخطاء المحلي. لكن تذكر: النتائج تتأثر بحمل CPU الحالي، لذا قم بعدة قياسات.
\n\n2. مثال عملي: إيجاد نقطة اختناق في خادم ويب
\nلنتخيل أن لدينا خادم HTTP بسيط على Express، يعالج الطلبات، ويحمل البيانات من ملف، ويقوم بحسابات معقدة. مهمتنا هي إيجاد سبب تباطؤ الاستجابات تحت الحمل.
\n\n2.1. الكود الأصلي (به مشكلة)
\nconst express = require('express');\nconst fs = require('fs');\nconst app = express();\n\napp.get('/data', (req, res) => {\n console.time('total');\n \n // قراءة الملف بشكل متزامن (عملية حظر)\n const fileData = fs.readFileSync('./large-file.json', 'utf8');\n \n // تحليل JSON\n const json = JSON.parse(fileData);\n \n // تحويل معقد\n const result = json.map(item => {\n let sum = 0;\n for (let i = 0; i < 10000; i++) {\n sum += Math.sqrt(item.value * i);\n }\n return { id: item.id, sum };\n })