تحليل أداء كود TypeScript: إيجاد نقاط الاختناق عملياً
\nواجه كل مطور موقفاً يبدأ فيه التطبيق بالتباطؤ دون سبب واضح. في عالم TypeScript، حيث يتم تحويل الكود إلى JavaScript، يمكن أن تنشأ نقاط الاختناق إما في مرحلة التنفيذ أو بسبب خصائص عملية التحويل نفسها. في هذه المقالة، سنستعرض طرقاً عملية لتحليل أداء كود TypeScript تساعد في تحديد الأجزاء الإشكالية وتحسين أداء تطبيقك.
\n\nلماذا يعتبر تحليل أداء TypeScript مهماً؟
\nيضيف TypeScript كتابة ثابتة، ولكن في وقت التشغيل يتم تنفيذ JavaScript عادي. هذا يعني أن نقاط الاختناق قد لا تكون مرتبطة فقط بالخوارزميات، بل أيضاً بكيفية ترجمة TypeScript. على سبيل المثال، الاستخدام المفرط للمزينات (decorators) أو الأنواع العامة المعقدة (generics) قد يؤدي إلى توليد كود غير أمثل. يساعد تحليل الأداء في الإجابة على الأسئلة التالية:
\n- \n
- ما هي الدوال التي تستغرق وقتاً أطول في التنفيذ؟ \n
- كم من الذاكرة يستهلك التطبيق؟ \n
- أين تحدث عمليات جمع القمامة (garbage collection) المتكررة؟ \n
- ما هي أجزاء الكود التي تمنع الخيط الرئيسي؟ \n
بدون تحليل الأداء، فإنك تخاطر بتحسين أجزاء غير مناسبة، مما يضيع وقتك في تحسينات دقيقة لا تحقق زيادة ملموسة في الأداء.
\n\nأدوات تحليل أداء TypeScript
\nلتحليل أداء كود TypeScript، سنستخدم ثلاث أدوات رئيسية تغطي معظم السيناريوهات: من تطبيقات المتصفح إلى حلول الخادم باستخدام Node.js.
\n\n1. Chrome DevTools لتطبيقات المتصفح
\nالأداة الأكثر سهولة وقوة. افتح علامة التبويب Performance في Chrome DevTools، ابدأ التسجيل وقم بتنفيذ الإجراء المطلوب في التطبيق. بعد الإيقاف، ستحصل على مخطط اللهب (flame chart) - تمثيل مرئي لاستدعاءات الدوال. إليك كيف يبدو ذلك عملياً:
\n// مثال على كود بطيء بلغة TypeScript\nfunction processData(data: number[]): number[] {\n const result: number[] = [];\n for (let i = 0; i < data.length; i++) {\n // نقطة اختناق محتملة: استدعاءات متكررة\n result.push(data[i] * Math.sqrt(data[i]));\n }\n return result;\n}\n\n// الاستخدام\nconst largeArray = Array.from({ length: 100000 }, (_, i) => i);\nconsole.time('processData');\nprocessData(largeArray);\nconsole.timeEnd('processData'); // ~15ms\n\nفي Chrome DevTools، سترى أن الدالة processData تستغرق وقتاً كبيراً، وداخلها العديد من استدعاءات Math.sqrt. هذا تلميح لإمكانية استبدال التكرار بـ map أو تحويل العمليات إلى صياغة متجهة.
2. Node.js profiler لتطبيقات الخادم
\nيحتوي Node.js على محلل أداء مدمج. قم بتشغيل التطبيق مع العلم --prof:
node --prof dist/app.js\n\nبعد الانتهاء من العمل، سيتم إنشاء ملف isolate-*.log. قم بتحويله إلى تنسيق قابل للقراءة:
node --prof-process isolate-*.log > processed.txt\n\nمثال على مشكلة نموذجية - التخصيصات المتكررة في الحلقات:
\n// مثال سيء: إنشاء كائنات في حلقة\ninterface User {\n id: number;\n name: string;\n}\n\nfunction createUsers(ids: number[]): User[] {\n const users: User[] = [];\n for (const id of ids) {\n users.push({ id, name: `User_${id}` }); // تخصيص جديد\n }\n return users;\n}\n\nسيظهر المحلل أن الدالة createUsers تنشئ العديد من الكائنات المؤقتة، مما يزيد الحمل على جامع القمامة. التحسين: تخصيص الذاكرة مسبقاً أو استخدام مجموعة كائنات (object pool).
3. Benchmark.js للمقاييس الصغيرة
\nعند الحاجة لمقارنة أداء تطبيقين، استخدم مكتبة benchmark.js. قم بتثبيتها عبر npm:
npm install benchmark @types/benchmark\n\nمثال على مقارنة طريقتين لتصفية مصفوفة:
\nimport Benc