صعود GitHub الشاق: تحسين خطوط الاختلاف لأقصى أداء
تعد طلبات السحب جوهر GitHub النابض بالحياة، حيث يكرس عدد لا يحصى من المهندسين جزءًا كبيرًا من حياتهم المهنية. نظرًا لحجم GitHub الهائل، والتعامل مع طلبات السحب التي تتراوح من الإصلاحات الطفيفة لسطر واحد إلى التغييرات الهائلة التي تمتد لآلاف الملفات وملايين الأسطر، يجب أن تظل تجربة المراجعة سريعة الاستجابة بشكل استثنائي. لقد كان الإطلاق الأخير لتجربة جديدة قائمة على React لعلامة التبويب الملفات المتغيرة، وهي الآن الافتراضية لجميع المستخدمين، استثمارًا محوريًا في ضمان الأداء القوي، خاصة لطلبات السحب الكبيرة الصعبة هذه. تضمن هذا الالتزام المعالجة المستمرة للمشكلات الصعبة مثل العرض الأمثل وزمن استجابة التفاعل واستهلاك الذاكرة.
قبل هذه التحسينات، بينما استمتع معظم المستخدمين بتجربة سريعة الاستجابة، أدت طلبات السحب الكبيرة حتمًا إلى تدهور ملحوظ في الأداء. وشهدت الحالات القصوى تجاوز ذاكرة كومة JavaScript 1 جيجابايت، وتجاوز عدد عقد DOM 400,000، وأصبحت تفاعلات الصفحة بطيئة للغاية أو حتى غير قابلة للاستخدام. ارتفعت مقاييس الاستجابة الرئيسية مثل التفاعل إلى عرض الطلاء التالي (INP) فوق المستويات المقبولة، مما خلق إحساسًا ملموسًا بتأخير الإدخال للمستخدمين. تتعمق هذه المقالة في الرحلة التفصيلية التي خاضتها GitHub لتحسين مقاييس الأداء الأساسية هذه بشكل كبير، مما أدى إلى تحويل تجربة مراجعة الاختلافات.
التنقل في اختناقات الأداء: نهج متعدد الاستراتيجيات
عند بدء التحقيق في أداء علامة التبويب الملفات المتغيرة، سرعان ما أصبح واضحًا أن حل 'الطلقة الفضية' الواحدة لن يكون كافيًا. غالبًا ما وصلت التقنيات المصممة للحفاظ على كل ميزة وسلوك متصفح أصلي إلى سقفها مع أحمال البيانات القصوى. وعلى العكس من ذلك، فإن التخفيفات التي تهدف فقط إلى منع أسوأ السيناريوهات قد تقدم مفاضلات غير مواتية للمراجعات اليومية.
بدلاً من ذلك، طور فريق الهندسة في GitHub مجموعة شاملة من الاستراتيجيات، صُممت كل منها بدقة لمعالجة أحجام وتعقيدات طلبات السحب المحددة. بُنيت هذه الاستراتيجيات على ثلاثة محاور أساسية:
- تحسينات مركزة لمكونات خطوط الاختلاف: تعزيز كفاءة تجربة الاختلاف الأساسية لغالبية طلبات السحب. هذا يضمن بقاء المراجعات المتوسطة والكبيرة سريعة دون المساس بالوظائف المتوقعة مثل البحث الأصلي في الصفحة.
- تقليل الأداء بلطف باستخدام الافتراضية: ضمان قابلية الاستخدام لأكبر طلبات السحب من خلال إعطاء الأولوية للاستجابة والاستقرار، والحد بذكاء مما يتم عرضه في أي لحظة معينة.
- الاستثمار في المكونات الأساسية وتحسينات العرض: تنفيذ تحسينات تحقق فوائد متراكمة عبر كل حجم طلب سحب، بغض النظر عن وضع عرض المستخدم المحدد.
وجهت هذه الركائز الاستراتيجية جهود الفريق، مما سمح لهم بمعالجة الأسباب الجذرية لمشكلات الأداء بشكل منهجي وتمهيد الطريق للتحسينات المعمارية اللاحقة.
تفكيك الإصدار الأول (V1): تكلفة سطر الاختلاف المكلف
وضع التنفيذ الأولي لـ GitHub القائم على React، والمشار إليه باسم الإصدار الأول (v1)، الأساس لعرض الاختلافات الحديث. كانت هذه النسخة جهدًا جادًا لنقل عرض Rails الكلاسيكي إلى React، مع إعطاء الأولوية لإنشاء مكونات React صغيرة قابلة لإعادة الاستخدام والحفاظ على هيكل شجرة DOM واضح. ومع ذلك، أثبت هذا النهج، رغم كونه منطقيًا في بدايته، أنه يمثل عنق الزجاجة كبيرًا عند التوسع.
في الإصدار الأول، كان عرض كل سطر اختلاف عملية مكلفة. عادة ما يترجم السطر الواحد في عرض موحد إلى حوالي 10 عناصر DOM، بينما يتطلب العرض المقسم ما يقرب من 15. سيزداد هذا العدد بشكل أكبر مع تسليط الضوء على بناء الجملة (syntax highlighting)، مما يقدم العديد من علامات <span> الإضافية. على طبقة React، احتوت الاختلافات الموحدة على ثمانية مكونات على الأقل لكل سطر، والعروض المقسمة على 13 مكونًا كحد أدنى. كانت هذه أعدادًا أساسية، مع حالات واجهة مستخدم إضافية مثل التعليقات، والتمرير، والتركيز، مما يضيف المزيد من المكونات.
كما عانت بنية الإصدار الأول من انتشار معالجات أحداث React. فبينما تبدو غير ضارة على نطاق صغير، يمكن لسطر اختلاف واحد أن يحمل 20 معالج أحداث أو أكثر. وعند ضرب هذا العدد عبر آلاف الأسطر في طلب سحب كبير، يتراكم هذا بسرعة، مما يؤدي إلى نفقات إضافية مفرطة وزيادة في استخدام ذاكرة كومة JavaScript. لم يؤثر هذا التعقيد على الأداء فحسب، بل جعل التطوير والصيانة أكثر صعوبة أيضًا. التصميم الأولي، الفعال للبيانات المحدودة، كافح بشكل كبير عندما واجه الطبيعة غير المحدودة لأحجام طلبات السحب المتنوعة في GitHub.
باختصار، لكل سطر اختلاف في الإصدار الأول، كان النظام يحتوي على:
- ما لا يقل عن 10-15 عنصرًا من شجرة DOM
- ما لا يقل عن 8-13 مكونًا من مكونات React
- ما لا يقل عن 20 معالج أحداث React
- عدد كبير من مكونات React الصغيرة القابلة لإعادة الاستخدام
ربطت هذه البنية بشكل مباشر أحجام طلبات السحب الأكبر بتباطؤ INP وزيادة استخدام ذاكرة كومة JavaScript، مما استلزم إعادة تقييم وإعادة تصميم أساسيين.
إحداث ثورة في العرض: تأثير تحسينات الإصدار الثاني (V2)
شكل الانتقال إلى الإصدار الثاني (v2) تحولًا معماريًا كبيرًا، مع التركيز على التغييرات الدقيقة والمؤثرة. تبنى الفريق الفلسفة القائلة بأن "لا يوجد تغيير صغير جدًا عندما يتعلق الأمر بالأداء، خاصة على نطاق واسع." وكان أحد الأمثلة الرئيسية هو إزالة علامات <code> غير الضرورية من خلايا أرقام الأسطر. فبينما قد يبدو إسقاط عقدتي DOM لكل سطر اختلاف أمرًا بسيطًا، إلا أنه عبر 10,000 سطر، يعادل هذا فورًا 20,000 عقدة أقل في DOM، مما يوضح كيف تؤدي التحسينات المستهدفة التدريجية إلى تحسينات جوهرية.
يوضح التباين البصري أدناه التعقيد المخفض من الإصدار الأول إلى الإصدار الثاني على مستوى المكونات:
مكونات الإصدار الأول (V1) لخطوط الاختلاف وHTML. كان لدينا 8 مكونات React لسطر اختلاف واحد.
مكونات الإصدار الثاني (V2) لخطوط الاختلاف وHTML. كان لدينا 3 مكونات React لسطر اختلاف واحد.
بنية المكونات المبسّطة
تمثلت إحدى الابتكارات الأساسية في الإصدار الثاني (v2) في تبسيط شجرة المكونات. انتقل الفريق من ثمانية مكونات React لكل سطر اختلاف إلى مكونين. وقد تحقق ذلك عن طريق إزالة أشجار المكونات المتداخلة بعمق وإنشاء مكونات مخصصة لكل سطر اختلاف مقسم وموحد. ورغم أن هذا أدى إلى بعض تكرار التعليمات البرمجية، إلا أنه بسّط الوصول إلى البيانات بشكل كبير وقلل التعقيد الكلي. كما تم مركزة معالجة الأحداث، وأصبحت الآن تُدار بواسطة معالج واحد على المستوى الأعلى يستخدم قيم data-attribute، ليحل محل معالجات الأحداث الفردية العديدة في الإصدار الأول. هذا النهج بسّط كل من التعليمات البرمجية والأداء بشكل جذري.
إدارة الحالة الذكية والوصول إلى البيانات بزمن ثابت O(1)
ربما كان التغيير الأكثر تأثيرًا هو نقل حالة التطبيق المعقدة، مثل التعليق وقوائم السياق، إلى مكونات فرعية يتم عرضها بشكل شرطي. في بيئة مثل GitHub، حيث يمكن أن تتجاوز طلبات السحب آلاف الأسطر، من غير الفعال أن يحمل كل سطر حالة تعليق معقدة بينما جزء صغير فقط هو الذي سيكون لديه تعليقات على الإطلاق. بنقل هذه الحالة إلى مكونات متداخلة، أصبحت المسؤولية الأساسية لمكون سطر الاختلاف هي عرض التعليمات البرمجية بحتة، مما يتوافق مع مبدأ المسؤولية الواحدة.
علاوة على ذلك، عالج الإصدار الثاني مشكلة عمليات البحث من نوع O(n) وخطافات useEffect المفرطة التي عانت منها v1. تبنى الفريق استراتيجية من جزأين: تقييد استخدام useEffect بشكل صارم على المستوى الأعلى لملفات الاختلاف وإنشاء قواعد linting لمنع إعادة تقديمها في مكونات التفاف الأسطر. وقد ضمن ذلك التخزين المؤقت الدقيق والسلوك المتوقع. في الوقت نفسه، أعيد تصميم آلات حالة الاختلاف والحالة العالمية للاستفادة من عمليات البحث بزمن ثابت O(1) باستخدام كائنات JavaScript Map. وقد سمح ذلك بمحددات سريعة ومتسقة للعمليات الشائعة مثل تحديد الأسطر وإدارة التعليقات، مما عزز بشكل كبير جودة التعليمات البرمجية، وحسّن الأداء، وقلل التعقيد عن طريق الحفاظ على هياكل بيانات مسطحة ومحددة. يضمن هذا النهج الدقيق لتحسين سير عمل المطورين والبنية التحتية نظامًا قويًا وقابلاً للتوسع.
التأثير القابل للقياس: الإصدار الثاني (V2) يحقق مكاسب كمية
أسفرت التحسينات المعمارية ودون مستوى التعليمات البرمجية الدقيقة التي تم تنفيذها في الإصدار الثاني عن تحسينات عميقة وقابلة للقياس عبر مقاييس الأداء الرئيسية. يعمل النظام الجديد بشكل أسرع بكثير، مع انخفاض هائل في استخدام ذاكرة كومة JavaScript ودرجات INP. يعرض الجدول التالي التحسينات الهائلة التي لوحظت على طلب سحب نموذجي يتضمن 10,000 تغيير في الأسطر في إعداد اختلاف مقسم:
| المقياس | الإصدار الأول (v1) | الإصدار الثاني (v2) | التحسن |
|---|---|---|---|
| ذاكرة كومة JavaScript | 1GB+ | 250MB | 75% |
| عقد DOM | 400,000+ | 80,000 | 80% |
| INP p95 | 1000ms+ | 100ms | 90% |
تؤكد هذه الأرقام نجاح استراتيجية GitHub متعددة الأوجه. إن خفض حجم ذاكرة كومة JavaScript بنسبة 75% وتقليل عقد DOM بنسبة 80% لا يترجم فقط إلى بصمة متصفح أخف، بل يساهم أيضًا بشكل مباشر في واجهة أكثر استقرارًا واستجابة. التحسن الأكثر إبهارًا، وهو تخفيض بنسبة 90% في INP p95 (الشريحة المئوية الخامسة والتسعون لزمن استجابة التفاعل)، يعني أن 95% من تفاعلات المستخدم تكتمل الآن في غضون 100 مللي ثانية فقط، مما يزيل عمليًا تأخر الإدخال الذي ابتليت به طلبات السحب الكبيرة في الإصدار الأول. يعزز هذا بشكل كبير تجربة المستخدم، مما يجعل مراجعات التعليمات البرمجية الكبيرة تبدو سلسة ومستجيبة مثل المراجعات الأصغر.
إن التزام GitHub بالتحسين المستمر، والذي يتضح من خلال هذا الغوص العميق في تحسين خطوط الاختلاف، هو شهادة على تفانيهم في توفير منصة تطوير عالمية المستوى. من خلال التحليل الدقيق لاختناقات الأداء وتطبيق حلول معمارية مستهدفة، لم يحلوا فقط مشكلات قابلية التوسع الحرجة، بل وضعوا أيضًا معيارًا جديدًا للاستجابة في منتجهم الأساسي. يضمن هذا التركيز على الأداء أن يتمكن المهندسون من الانخراط بكفاءة في المهام الحاسمة مثل مراجعات التعليمات البرمجية، مما يؤدي في النهاية إلى جودة وأمان أعلى للتعليمات البرمجية وبيئة تطوير أكثر إنتاجية.
المصدر الأصلي
https://github.blog/engineering/architecture-optimization/the-uphill-climb-of-making-diff-lines-performant/الأسئلة الشائعة
What is the 'Files changed' tab in GitHub pull requests and why was its performance critical?
What were the primary performance challenges GitHub faced with large pull requests in the v1 architecture?
How did GitHub approach solving the complex performance issues, moving beyond a 'silver bullet' solution?
What were the key limitations of the 'v1' diff rendering architecture that made it unsustainable for scale?
What specific architectural changes were implemented in 'v2' to drastically improve diff line performance?
How did the GitHub engineering team achieve quantifiable improvements in JavaScript heap, DOM nodes, and INP metrics with v2?
What is Interaction to Next Paint (INP) and why is its improvement significant for GitHub's user experience?
ابقَ على اطلاع
احصل على آخر أخبار الذكاء الاصطناعي في بريدك.
