سؤال

في حين أنه سيكون من المناسب جدًا استخدام الوظائف المضمنة في بعض المواقف،

هل هناك أي عيوب مع الوظائف المضمنة؟

خاتمة:

على ما يبدو، لا حرج في استخدام الوظائف المضمنة.

ولكن تجدر الإشارة إلى النقاط التالية!

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

  • تميل فوائد السرعة للوظائف المضمنة إلى التناقص مع نمو حجم الوظيفة.في مرحلة ما، يصبح الحمل الزائد لاستدعاء الوظيفة صغيرًا مقارنة بتنفيذ نص الوظيفة، ويتم فقدان الفائدة - مصدر

  • هناك حالات قليلة قد لا تعمل فيها الوظيفة المضمنة:

    • بالنسبة لدالة تقوم بإرجاع القيم؛إذا كان هناك بيان الإرجاع.
    • بالنسبة للدالة التي لا تُرجع أي قيم؛في حالة وجود حلقة أو عبارة التبديل أو goto.
    • إذا كانت الوظيفة متكررة. -مصدر
  • ال __inline تؤدي الكلمة الأساسية إلى تضمين الوظيفة فقط إذا قمت بتحديد خيار التحسين.إذا تم تحديد التحسين، أم لا __inline يتم تكريمه بناءً على إعداد خيار المحسن المضمن.بشكل افتراضي، يكون الخيار المضمن ساري المفعول عند تشغيل المحسن.إذا قمت بتحديد التحسين، فيجب عليك أيضًا تحديد خيار noinline إذا كنت تريد __inline الكلمة الأساسية التي يجب تجاهلها. -مصدر

هل كانت مفيدة؟

المحلول

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

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

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

نصائح أخرى

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

أعتقد أنه عادة ما يكون على ما يرام إذا كانت الوظيفة تحتوي على بيان واحد أو 2 فقط.

يحرر: وهنا ما لينكس CodingStyle الوثيقة تقول عن ذلك:

الفصل 15:المرض المضمن

يبدو أن هناك اعتقادًا كبيرًا بأن لدى GCC خيار تسريع أسرع "يجعلني أسرع" "INLINE".على الرغم من أن استخدام الخطوط الداخلية يمكن أن يكون مناسبًا (على سبيل المثال كوسيلة لاستبدال وحدات الماكرو ، انظر الفصل 12) ، فغالبًا ما لا يكون ذلك كذلك.يؤدي الاستخدام الوفير للكلمة الرئيسية المضمنة إلى نواة أكبر بكثير ، والتي بدورها تبطئ النظام ككل ، نظرًا لوجود بصمة أكبر في وحدة المعالجة المركزية وببساطة لأن هناك ذاكرة أقل متاحة لـ Pagecache.مجرد التفكير في ذلك؛تسبب ملكة جمال PageCache تسعى القرص ، والذي يستغرق بسهولة 5 ميلي ثانية.هناك الكثير من دورات وحدة المعالجة المركزية التي يمكن أن تدخل في هذه الألواح الخمسة.

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

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

وأنا أتفق مع الوظائف الأخرى:

  • قد يكون inline غير ضروري لأن المترجم سيقوم بذلك
  • قد يؤدي inline إلى تضخم التعليمات البرمجية الخاصة بك

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

class OtherObject;

class Object {
public:
    void someFunc(OtherObject& otherObj) {
        otherObj.doIt(); // Yikes requires OtherObj declaration!
    }
};

بدون المضمنة، كان الإعلان الأمامي لـ OtherObject هو كل ما تحتاجه.مع وجود رأسك يحتاج إلى تعريف آخر.

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

إذا كنت تريد حقًا أن تجعل شيئًا ما مضمّنًا، وإذا كنت قد قمت بتوصيفه فعليًا ونظرت إلى التفكيك للتأكد من أن تجاوز إرشاد المترجم منطقي بالفعل، فمن الممكن:

  • في VC++، استخدم الكلمة الأساسية __forceinline
  • في دول مجلس التعاون الخليجي، استخدم __attribute__((always_inline))

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

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

أنا أشك في ذلك.حتى المترجم يقوم تلقائيًا بتضمين بعض الوظائف للتحسين.

لا أعلم إن كانت إجابتي مرتبطة بالسؤال ولكن:

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

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

إن تحديد متى تكون الوظيفة صغيرة بدرجة كافية بحيث يؤدي التضمين إلى زيادة الأداء أمر صعب للغاية. دليل أسلوب Google C++ توصي فقط بتضمين الوظائف المكونة من 10 أسطر أو أقل.

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

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

باستخدام برنامج التحويل البرمجي VC++، يمكنك تجاوز هذا القرار باستخدام __forceinline

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

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

من بين المشكلات الأخرى المتعلقة بالوظائف المضمنة، والتي رأيت الإفراط في استخدامها بشكل كبير (لقد رأيت وظائف مضمنة مكونة من 500 سطر)، ما يجب أن تكون على دراية به هو:

  • بناء عدم الاستقرار

    • يؤدي تغيير مصدر الدالة المضمنة إلى إعادة ترجمة جميع مستخدمي الرأس
    • #includeتسرب إلى العميل.يمكن أن يكون هذا أمرًا سيئًا للغاية إذا قمت بإعادة صياغة وظيفة مضمنة وإزالة رأس لم يعد مستخدمًا والذي اعتمد عليه بعض العملاء.
  • الحجم القابل للتنفيذ

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

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

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

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

  2. هناك عدد قليل من المواقف الأخرى التي قد لا تعمل فيها المضمنة

    1. لا يعمل في حالة وظيفة العودية.
    2. وقد لا يعمل أيضًا مع المتغير الثابت.
    3. كما أنه لا يعمل في حالة استخدام حلقة أو مفتاح وما إلى ذلك. أو يمكننا قول ذلك باستخدام عبارات متعددة.
    4. ولا يمكن للوظيفة الرئيسية أن تعمل كوظيفة مضمنة.
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top