تحسين الكود: الطريقة المثلى للتحسين

نشرت: 2022-03-11

يعد تحسين الأداء أحد أكبر التهديدات التي تواجه التعليمات البرمجية الخاصة بك.

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

ليس فقط لتمييز نفسك عن الآخرين كمطور أفضل. ليس فقط لتجنب أن تكون "دان" على The Daily WTF ، ولكن لأنك تعتقد أن تحسين الكود هو الشيء الصحيح الذي يجب القيام به. أنت تفتخر بعملك.

تزداد سرعة أجهزة الكمبيوتر ، وتصنع البرامج أسهل ، ولكن مهما كان الشيء البسيط الذي تريد فقط أن تكون قادرًا على القيام به ، فإن Dammit تستغرق دائمًا وقتًا أطول من السابق. إنك تهز رأسك في هذه الظاهرة (بالمناسبة ، المعروفة باسم قانون ويرث) وتعتزم التغلب على هذا الاتجاه.

هذا نبيل منك ، لكن توقف.

توقف!

أنت في خطر كبير يتمثل في إحباط أهدافك ، بغض النظر عن مدى خبرتك في البرمجة.

كيف ذلك؟ دعنا نعود.

بادئ ذي بدء ، ما هو تحسين الكود؟

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

من الناحية العملية ، نفترض أحيانًا استخدام تعريف آخر: كتابة كود أقل.

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

لعبة الكود: + 197٪ ، الأداء: -398٪ ، البساطة: -9999٪

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

لنبدأ بالاستماع إلى نصيحة الحكماء بينما نستكشف معًا قواعد تحسين كود جاكسون الشهيرة:

  1. لا تفعل ذلك.
  2. (للخبراء فقط!) لا تفعل ذلك بعد .

1. لا تفعل ذلك: توجيه السعي إلى الكمال

سأبدأ بمثال متطرف محرج إلى حد ما من وقت ، منذ فترة طويلة ، كنت أتبلل قدمي في عالم SQL الرائع. كانت المشكلة هي أنني بعد ذلك داس على الكعكة ولم أرغب في تناولها بعد الآن لأنها كانت مبتلة وبدأت رائحتها مثل القدمين.

لقد بدأت للتو ببلل قدمي في عالم SQL الرائع. كانت المشكلة ، ثم خطوت على الكعكة ...

انتظر. اسمحوا لي أن أعود من حطام السيارة لاستعارة صنعتها وشرحتها للتو.

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

أن كل شيء توقف ، من حيث التطوير والأداء ، بسبب ... كنت تفكر في ذلك ، التحسين.

لقد رأيت أن الكائنات (ممثلة بصفوف جدول) يمكن أن يكون لها العديد من العلاقات المختلفة مع بعضها البعض في العالم الحقيقي وأنه يمكننا الاستفادة من تتبع هذه العلاقات: سنحتفظ بمزيد من المعلومات ويمكننا في النهاية أتمتة تحليل الأعمال في كل مكان. نظرًا لأن هذا يمثل مشكلة هندسية ، فقد فعلت شيئًا بدا وكأنه تحسين لمرونة النظام.

في هذه المرحلة ، من المهم الانتباه إلى وجهك ، لأنني لن أتحمل المسؤولية إذا كان راحة يدك تؤلمه. مستعد؟ لقد أنشأت جدولين: relationship وأحدهما يحتوي على مرجع لمفتاح خارجي ، نوع relationship_type . يمكن أن تشير relationship إلى أي صفين في أي مكان في قاعدة البيانات بأكملها ، وتصف طبيعة العلاقة بينهما.

جداول قاعدة البيانات: موظف ، شركة ، علاقة ، نوع العلاقة

يا رجل. لقد قمت للتو بتحسين تلك المرونة كثيرًا .

كثيرا ، في الواقع. الآن لدي مشكلة جديدة: نوع relationship_type معين لن يكون له معنى بطبيعة الحال بين كل مجموعة من الصفوف. في حين أنه قد يكون من المنطقي أن يكون لدى person ما employed by علاقة company ، إلا أن ذلك لا يمكن أن يكون معادلاً لغويًا للعلاقة بين ، على سبيل المثال ، document .

حسنا لا مشكلة. سنضيف عمودين فقط إلى نوع relationship_type ، مع تحديد الجداول التي يمكن تطبيق هذه العلاقة عليها. (نقاط المكافأة هنا إذا كنت أعتقد أنني فكرت في تسوية هذا عن طريق نقل هذين العمودين إلى جدول جديد يشير إلى relationship_type.id ، بحيث لا تتكرر أسماء الجداول التي يمكن أن تنطبق معنويًا على أكثر من زوج واحد من الجداول. بعد كل شيء ، إذا كنت بحاجة إلى تغيير اسم الجدول ونسيت تحديثه في جميع الصفوف القابلة للتطبيق ، فقد يؤدي ذلك إلى حدوث خطأ! في الماضي ، على الأقل كانت الحشرات ستوفر الطعام للعناكب التي تعيش في جمجمتي.)

جداول قاعدة البيانات: نوع العلاقة و القابل للتطبيق ، والبيانات المعقدة لعمودين من نوع العلاقة ممثلة بالسهم

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

استخدم المفاتيح الخارجية ، لوقا!

دعنا نعود لمدة ثانية ونلقي نظرة على مقياسين من العديد من المقاييس قيد التشغيل هنا. الأول هو المرونة ، التي كانت هدفي المعلن. في هذه الحالة ، لم يكن التحسين ، لكوني معماريًا بطبيعته ، سابقًا لأوانه:

خطوات تحسين الكود: الهندسة المعمارية هي الجزء الأول من البرنامج الذي يتم تحسينه

(سنتطرق إلى الأمر أكثر في مقالتي المنشورة مؤخرًا ، كيفية تجنب لعنة التحسين المبكر.) ومع ذلك ، فشل الحل بشكل مذهل من خلال كونه مرنًا للغاية . المقياس الآخر ، قابلية التوسع ، كان أحد المقاييس التي لم أكن أفكر فيها حتى الآن ، لكنني تمكنت من تدميرها على الأقل بشكل مذهل مع الأضرار الجانبية.

هذا صحيح ، "أوه".

ضعف راحة اليد ، لأنه عندما لا يقطعها كف واحد

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

حسِّن عاداتك وليس كودك

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

  1. حماية
  2. استقرار وقت التشغيل
  3. الوضوح والأناقة
  4. كفاءة الترميز
  5. فعالية الاختبار
  6. التنميط
  7. مجموعة الأدوات الخاصة بك / DE
  8. جاف (لا تكرر نفسك)

لكن احذر: تحسين الخروج من أي واحد من هؤلاء سيأتي على حساب الآخرين. على أقل تقدير ، يأتي على حساب الوقت.

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

على سبيل المثال ، فيما يتعلق بـ DRY: في إحدى الوظائف التي كنت أمارسها ، ورثت قاعدة بيانات كانت على الأقل 80٪ عبارات زائدة عن الحاجة ، لأن مؤلفها على ما يبدو لم يكن على دراية بكيفية ووقت كتابة دالة. 20٪ الأخرى من الكود كانت متشابهة مع ذاتها بشكل مربك.

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

من الواضح ، أنه كان بحاجة إلى إعادة هيكلة فقط من أجل سلامة عقلي (قيمة عالية) ولأي مطورين مستقبليين. ولكن ، نظرًا لأنني كنت جديدًا على قاعدة الشفرات ، فقد قمت أولاً بكتابة الاختبارات حتى أتمكن من التأكد من أن إعادة التعميم الخاصة بي لم تحدث أي انحدار. في الواقع ، لقد فعلوا ذلك بالضبط: لقد اكتشفت خطأين على طول الطريق لم أكن لألاحظه بين جميع مخرجات gobbledygook التي أنتجها النص.

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

احذر: تحسين الخروج من أي [جانب] معين سيأتي على حساب الآخرين. على أقل تقدير ، يأتي على حساب الوقت.

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

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

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

اتضح ، بعد بضع ساعات من التصحيح غير المجدي ، أن هذا لم يكن ممكنًا حاليًا مع محرك القالب بالطريقة التي تخيلتها. لم يكن هناك حل جاف مثالي فحسب ؛ لم يكن هناك أي حل جاف على الإطلاق!

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

حتى في ذلك الوقت ، هل كنت مخطئًا تمامًا؟ في بعض الأحيان ، يكون الأمر يستحق القليل من الاستثمار ، لا سيما في سياق تقني جديد ، للتعرف على أفضل الممارسات في وقت مبكر وليس لاحقًا. رمز أقل لإعادة الكتابة والعادات السيئة للتراجع ، أليس كذلك؟

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

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

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

ماذا عن تلك القاعدة الثانية؟ متى يمكننا فعلا التحسين؟

2. لا تفعل ذلك حتى الآن : شخص ما فعل هذا بالفعل

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

لنأخذ خطوة أبعد من ذلك: لا تفعل ذلك بعد : لا تقم حتى الآن بتشفيرها .

قد يكون هذا بحد ذاته رائحته مثل التحسين المبكر ، لكنه استثناء مهم. لماذا ا؟ لتجنب متلازمة NIHS المخيفة ، أو متلازمة "لم يتم اختراعها هنا" - بافتراض أن أولوياتك تشمل أداء الكود وتقليل وقت التطوير. إذا لم يكن الأمر كذلك ، إذا كانت أهدافك موجهة نحو التعلم تمامًا ، فيمكنك تخطي هذا القسم التالي.

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

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

(تعد واجهة برمجة تطبيقات Java Calendar القديمة نقطة مقابلة جيدة لهذا النمط العام ، ولكن تم إصلاحها منذ ذلك الحين.)

تحقق من مكتبتك القياسية ، وتحقق من النظام البيئي لإطار العمل الخاص بك ، وتحقق من البرمجيات الحرة والمفتوحة المصدر التي تحل مشكلتك بالفعل

هناك احتمالات ، أن المفاهيم التي تتعامل معها لها أسماء قياسية ومعروفة ، لذا فإن البحث السريع على الإنترنت سيوفر لك الكثير من الوقت.

على سبيل المثال ، كنت أستعد مؤخرًا لإجراء بعض التحليلات لاستراتيجيات الذكاء الاصطناعي للعبة اللوحة. استيقظت ذات صباح وأنا أدرك أن التحليل الذي كنت أخطط له يمكن أن يتم بأحجام أكبر بكفاءة إذا استخدمت ببساطة مفهومًا اندماجيًا معينًا أتذكره. لم أكن مهتمًا باكتشاف الخوارزمية لهذا المفهوم بنفسي في هذا الوقت ، كنت متقدمًا بالفعل من خلال معرفة الاسم الصحيح للبحث عنه. ومع ذلك ، وجدت أنه بعد حوالي 50 دقيقة من البحث وتجربة بعض التعليمات البرمجية الأولية ، لم أتمكن من تحويل الكود الزائف نصف النهائي الذي وجدته إلى تطبيق صحيح. (هل تصدق أن هناك منشور مدونة حيث يفترض المؤلف أن ناتج خوارزمية غير صحيح ، وينفذ الخوارزمية بشكل غير صحيح لمطابقة الافتراضات ، ويشير المعلقون إلى ذلك ، وبعد ذلك بسنوات ، لا يزال الأمر غير ثابت؟) في تلك المرحلة ، شاي الصباح الخاص بي بدأت ، وبحثت عن [name of concept] [my programming language] . بعد 30 ثانية ، كان لدي رمز صحيح مثبت من GitHub وكنت أنتقل إلى ما كنت أرغب بالفعل في القيام به. مجرد التحديد وإدراج اللغة ، بدلاً من افتراض أنني سأضطر إلى تنفيذها بنفسي ، يعني كل شيء.

حان الوقت لتصميم هيكل البيانات الخاصة بك وتنفيذ الخوارزمية الخاصة بك

... مرة أخرى ، لا تلعب لعبة الكود. إعطاء الأولوية للصحة والوضوح في مشاريع العالم الحقيقي.

استثمار الوقت: 10 ساعات، وقت التنفيذ: + 25٪، استخدام الذاكرة: + 3٪، الارتباك: 100٪

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

لا مشكلة. النصيحة بسيطة ، بهذا الترتيب:

  1. صممه بحيث يكون من السهل شرحه للمبرمج المبتدئ.
  2. اكتب اختبارًا يناسب التوقعات الناتجة عن هذا التصميم.
  3. اكتب الكود الخاص بك حتى يتمكن المبرمج المبتدئ من استخلاص التصميم منه بسهولة.

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

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

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

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

مثال ECMAScript

في موقع Practiceism.io الممتاز لمراجعة التعليمات البرمجية للمجتمع ، وجدت مؤخرًا تمرينًا اقترح صراحةً محاولة إما تحسين إزالة الازدواجية أو الوضوح. لقد قمت بالتحسين لإلغاء البيانات المكررة ، فقط لإظهار كيف يمكن أن تحصل الأشياء السخيفة إذا تناولت DRY - عقلية تشفير مفيدة بخلاف ذلك ، كما ذكرت أعلاه - بعيدًا جدًا. هذا هو شكل الكود الخاص بي:

 const zeroPhrase = "No more"; const wallPhrase = " on the wall"; const standardizeNumber = number => { if (number === 0) { return zeroPhrase; } return '' + number; } const bottlePhrase = number => { const possibleS = (number === 1) ? '' : 's'; return standardizeNumber(number) + " bottle" + possibleS + " of beer"; } export default class Beer { static verse(number) { const nextNumber = (number === 0) ? 99 : (number - 1); const thisBottlePhrase = bottlePhrase(number); const nextBottlePhrase = bottlePhrase(nextNumber); let phrase = thisBottlePhrase + wallPhrase + ", " + thisBottlePhrase.toLowerCase() + ".\n"; if (number === 0) { phrase += "Go to the store and buy some more"; } else { const bottleReference = (number === 1) ? "it" : "one"; phrase += "Take " + bottleReference + " down and pass it around"; } return phrase + ", " + nextBottlePhrase.toLowerCase() + wallPhrase + ".\n"; } static sing(start = 99, end = 0) { return Array.from(Array(start - end + 1).keys()).map(offset => { return this.verse(start - offset); }).join('\n'); } }

بالكاد أي ازدواجية في السلاسل هناك على الإطلاق! من خلال كتابتها بهذه الطريقة ، قمت يدويًا بتنفيذ أحد أشكال ضغط النص لأغنية البيرة (ولكن فقط لأغنية البيرة). ماذا كانت الفائدة بالضبط؟ حسنًا ، لنفترض أنك تريد الغناء عن شرب البيرة من العلب بدلاً من الزجاجات. يمكنني تحقيق ذلك عن طريق تغيير مثيل واحد من bottle إلى can .

لطيف - جيد!

…الصحيح؟

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

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

حكمة التدقيق في المستقبل بهذه الطريقة مشكوك فيها إلى حد كبير. ما هي احتمالات تغيير أي شيء على الإطلاق؟ وإذا فعلت ذلك ، فهل سينجح ما ستغيره بسهولة؟ في مثال bottlePhrase ، ماذا لو كنت تريد الترجمة إلى لغة بها أكثر من صيغتين للجمع؟ هذا صحيح ، وقت إعادة البناء ، وقد يبدو الرمز أسوأ بعد ذلك.

ولكن عندما تتغير متطلباتك ، ولا تحاول فقط توقعها ، فربما حان الوقت لإعادة البناء. أو ربما لا يزال بإمكانك تأجيلها: كم عدد أنواع السفن أو المواقع التي ستضيفها ، بشكل واقعي؟ على أي حال ، عندما تحتاج إلى الموازنة بين إزالة البيانات المكررة والوضوح ، فإن الأمر يستحق مشاهدة هذا العرض التوضيحي لكاترينا أوين.

العودة إلى المثال القبيح الخاص بي: وغني عن القول ، لم تتحقق فوائد إلغاء البيانات المكررة هنا كثيرًا. في غضون ذلك ، ما هي تكلفة ذلك؟

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

لكننا ما زلنا غير محسّن!

الآن بعد أن تم تنفيذ الخوارزمية الخاصة بك ، وأثبتت أن مخرجاتها صحيحة ، تهانينا! لديك أساس!

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

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

اذهب لإنهاء تطبيقك (أو مكونك) ، إذا لم تكن قد قمت بذلك بالفعل ، فقم بتعيين جميع خطوط الأساس المعيارية الخوارزمية كما تذهب.

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

ربما ستجد أن كل شيء على ما يرام.

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

حسنًا ، يمكنك الآن التحسين

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

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

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

الآن بعد أن استخدمت التنميط لتحديد عنق الزجاجة في نظامك ، يمكنك الآن محاولة التحسين ، واثقًا من أن التحسين يستحق القيام به. يمكنك أيضًا إثبات مدى فاعلية (أو عدم فعالية) محاولتك ، بفضل تلك المعايير الأساسية التي قمت بها على طول الطريق.

التقنيات الشاملة

أولاً ، تذكر أن تظل على مستوى عالٍ لأطول فترة ممكنة:

على مستوى الخوارزمية الكاملة ، تتمثل إحدى التقنيات في تقليل القوة. في حالة اختزال الحلقات إلى الصيغ ، ضع في اعتبارك ترك التعليقات. لا يعرف الجميع أو يتذكر كل صيغة توافقية. كن حذرًا أيضًا في استخدامك للرياضيات: أحيانًا ما تعتقد أنه قد يكون تقليل القوة ليس كذلك في النهاية. على سبيل المثال ، لنفترض أن x * (y + z) لها معنى حسابي واضح. إذا تم تدريب عقلك في مرحلة ما ، لأي سبب من الأسباب ، على إلغاء تجميع المصطلحات المتشابهة تلقائيًا ، فقد تميل إلى إعادة كتابة ذلك كـ x * y + x * z . لسبب واحد ، هذا يضع حاجزًا بين القارئ والمعنى الحسابي الواضح الذي كان هناك. (والأسوأ من ذلك ، أنها الآن أقل كفاءة في الواقع بسبب عملية الضرب الإضافية المطلوبة. إنها تشبه فتح الحلقة لتكتل بنطالها.) على أي حال ، فإن ملاحظة سريعة حول نواياك ستقطع شوطًا طويلاً ، وقد تساعدك أيضًا على رؤية خطأ قبل أن ترتكبه.

سواء كنت تستخدم صيغة أو مجرد استبدال خوارزمية قائمة على حلقة بخوارزمية أخرى قائمة على حلقة ، فأنت على استعداد لقياس الفرق.

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

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

التحسينات الدقيقة

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

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

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

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

ربما لا تسلك هذا الطريق. في هذه الحالة ، قد يكون من المفيد استكشاف فئات مختلفة من تقنيات تحسين الكود:

  • التخزين المؤقت
  • خارقة البت وتلك الخاصة ببيئات 64 بت
  • حلقة التحسين
  • تحسين التسلسل الهرمي للذاكرة

اكثر تحديدا:

  • نصائح لتحسين الكود في C و C ++
  • نصائح لتحسين التعليمات البرمجية في Java
  • تحسين استخدام وحدة المعالجة المركزية في .NET
  • التخزين المؤقت لمزرعة الويب ASP.NET
  • قاعدة بيانات SQL لضبط Microsoft SQL Server أو ضبطه على وجه الخصوص
  • تحجيم اللعب سكالا! إطار العمل
  • تحسين أداء ووردبريس المتقدم
  • تحسين الكود باستخدام نموذج JavaScript الأولي وسلاسل النطاق
  • تحسين أداء التفاعل
  • كفاءة الرسوم المتحركة على iOS
  • نصائح حول أداء Android

على أي حال ، لدي بعض الأمور المحظورة لك:

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

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

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

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

إنها لك: التحسين الأمثل

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

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