c++ متى يجب عليّ كتابة الكلمة الأساسية "inline" لوظيفة/طريقة؟




one-definition-rule (10)

متى يجب علي أن أكتب الكلمة الرئيسية للدالة / الطريقة في C ++؟

بعد رؤية بعض الإجابات ، بعض الأسئلة ذات الصلة:

  • متى لا أكتب الكلمة الأساسية "inline" لوظيفة / طريقة في C ++؟

  • متى لا يعرف المترجم متى يقوم بعمل / طريقة "مضمنة"؟

  • هل يهم إذا كان أحد تطبيقات متعددة مؤشرات ترابط عند كتابة "مضمنة" دالة / أسلوب؟


  • متى لا يعرف المترجم متى يقوم بعمل / طريقة "مضمنة"؟

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

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

(مزيد من التفاصيل: محاكاة الرياضيات مع وظائف حرجة قليلة محددة خارج الفصل ، GCC 4.6.3 (g ++ -O3) ، ICC 13.1.0 (icpc -O3) ؛ إضافة مضمنة للنقاط الحرجة تسبب + 6٪ تسريع مع رمز GCC).

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


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

(ولكن انظر هل هناك أي سبب لعدم استخدام تحسين وقت الرابط؟ )


عندما يجب على المرء أن يكون مضمنًا:

1. عندما يرغب المرء في تجنب الأشياء التي تحدث عند استدعاء الوظيفة مثل تمرير المعلمة ، ونقل السيطرة ، وعودة السيطرة الخ.

2. يجب أن تكون الوظيفة صغيرة ، وتسمى في كثير من الأحيان وجعل مضمنة هو مفيد حقا حيث أنه وفقا للقاعدة 80-20 ، في محاولة لجعل تلك الوظيفة المضمنة التي لها تأثير كبير على أداء البرنامج.

كما نعلم أن المضمنة هو مجرد طلب مترجم مماثل للتسجيل ، وسوف يكلفك في حجم رمز الكائن.


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

عموما يقوم المترجم بعمل جيد للكشف عن + تحسين مثل هذه الأمور.


يا رجل ، واحدة من بيبيز.

inline أكثر مثل static أو extern من توجيه إخباري المحول البرمجي لتضمين وظائفك. extern ، static ، inline هي توجيهات الارتباطات ، المستخدمة تقريبًا تقريبًا بواسطة linker وليس المحول البرمجي.

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

  • static - لا يمكن استخدام اسم المتغير / الوظيفة في وحدات الترجمة الأخرى. يحتاج رابط التأكد من أنه لا يستخدم بطريق الخطأ متغير / وظيفة محددة بشكل ثابت من وحدة ترجمة أخرى.

  • extern - استخدم اسم المتغير / الوظيفة هذا في وحدة الترجمة هذه ولكن لا تتذمر إذا لم يتم تعريفها. سيقوم الرابط بفرزها والتأكد من أن كافة التعليمات البرمجية التي حاولت استخدام بعض رمز extern لها عنوانها.

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

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

إجابات محددة على أسئلتك:

  • متى يجب عليّ كتابة الكلمة الأساسية "inline" لوظيفة / طريقة في C ++؟

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

  • متى لا أكتب الكلمة الأساسية "inline" لوظيفة / طريقة في C ++؟

    لا تضيف مضمنة فقط لأنك تعتقد أن شفرتك ستعمل بشكل أسرع إذا كان المترجم يبرزها.

  • متى لا يعرف المترجم متى يقوم بعمل / طريقة "مضمنة"؟

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

    كمفتاح لمنع inlining في دول مجلس التعاون الخليجي ، استخدم __attribute__(( noinline )) ، وفي Visual Studio ، استخدم __declspec(noinline) .

  • هل يهم إذا كان أحد تطبيقات متعددة مؤشرات ترابط عند كتابة "مضمنة" دالة / أسلوب؟

    لا يؤثر multithreading بالتضمين بأي طريقة.


أود الإسهام في جميع الإجابات العظيمة في هذا الموضوع مع مثال مقنع لتفريق أي سوء فهم متبقٍ.

بالنظر إلى ملفين مصدرين ، مثل:

  • inline111.cpp:

    #include <iostream>
    
    void bar();
    
    inline int fun() {
      return 111;
    }
    
    int main() {
      std::cout << "inline111: fun() = " << fun() << ", &fun = " << (void*) &fun;
      bar();
    }
    
  • inline222.cpp:

    #include <iostream>
    
    inline int fun() {
      return 222;
    }
    
    void bar() {
      std::cout << "inline222: fun() = " << fun() << ", &fun = " << (void*) &fun;
    }
    
  • الحالة أ:

    ترجمة :

    g++ -std=c++11 inline111.cpp inline222.cpp
    

    الإخراج :

    inline111: fun() = 111, &fun = 0x4029a0
    inline222: fun() = 111, &fun = 0x4029a0
    

    مناقشة :

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

    2. لا يشتكي Linker من One Definition Rule ، حيث يتم الإعلان عن fun() على هيئة inline . ومع ذلك ، لأن inline111.cpp هو أول وحدة ترجمة (فعلياً يستدعي fun() ) تتم معالجتها بواسطة برنامج التحويل البرمجي ، instantiates compiler fun() عند أول استدعاء له في inline111.cpp . إذا قرر المترجم عدم زيادة fun() على مكالمته من أي مكان آخر في البرنامج (على سبيل المثال من inline222.cpp ) ، فسيتم دائمًا ربط المكالمة fun() مع مثيله الناتج من inline111.cpp (استدعاء fun() داخل inline222.cpp قد ينتج أيضًا مثيل في وحدة الترجمة هذه ، ولكنه سيظل غير مرتبط). في الواقع ، هذا هو واضح من &fun = 0x4029a0 .

    3. وأخيرًا ، على الرغم من الاقتراح inline للمترجم للتوسع فعليًا في fun() الواحد fun() ، فإنه يتجاهل اقتراحك تمامًا ، وهو أمر واضح لأن fun() = 111 في كلا الخطين.

  • الحالة ب:

    ترجمة (لاحظ ترتيب عكسي) :

    g++ -std=c++11 inline222.cpp inline111.cpp
    

    الإخراج :

    inline111: fun() = 222, &fun = 0x402980
    inline222: fun() = 222, &fun = 0x402980
    

    مناقشة :

    1. تؤكد هذه الحالة ما تم مناقشته في القضية أ .

    2. لاحظ نقطة مهمة ، إذا قمت بالتعليق على المكالمة الفعلية fun() في inline222.cpp (على سبيل المثال ، التعليق على cout -statement in inline222.cpp بالكامل) ، على الرغم من ترتيب تجميع وحدات الترجمة الخاصة بك ، فإن fun() ستكون يتم إنشاء مثيله في الاتصال الأول في inline111.cpp ، مما يؤدي إلى طباعة حالة inline111: fun() = 111, &fun = 0x402980 B في inline111: fun() = 111, &fun = 0x402980 .

  • الحالة ج:

    ترجمة (إشعار -O2) :

    g++ -std=c++11 -O2 inline222.cpp inline111.cpp
    

    أو

    g++ -std=c++11 -O2 inline111.cpp inline222.cpp
    

    الإخراج :

    inline111: fun() = 111, &fun = 0x402900
    inline222: fun() = 222, &fun = 0x402900
    

    مناقشة :

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

لا يضيف gcc افتراضيًا أي وظائف عند التحويل البرمجي دون تمكين التحسين. لا أعرف عن الاستوديو المرئي - deft_code

راجعت هذا لـ Visual Studio 9 (15.00.30729.01) عن طريق التحويل البرمجي مع / FAcs والنظر إلى رمز التجميع: ينتج المترجم المكالمات إلى وظائف الأعضاء دون تمكين التحسين في وضع التصحيح . حتى إذا تم وضع علامة على الدالة بـ __forceinline ، لا يتم إنشاء رمز وقت التشغيل المضمن.


لا تزال تحتاج إلى تضمين الدالة بشكل صريح عند القيام بتخصص القالب (إذا كان التخصص في الملف .h)


1) في الوقت الحاضر ، إلى حد كبير أبدا أبدا. إذا كان من الأفضل تضمين وظيفة ما ، فسوف يقوم المترجم بذلك دون مساعدتك.

2) دائما. انظر رقم 1.

(تم تعديله ليعكس أنك قمت بكسر سؤالك في سؤالين ...)


متى لا أكتب الكلمة الأساسية "inline" لوظيفة / طريقة في C ++؟

إذا تم تعريف الدالة في ملف .cpp ، يجب عدم كتابة الكلمة الأساسية.

متى لا يعرف المترجم متى يقوم بعمل / طريقة "مضمنة"؟

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

هل يهم إذا كان أحد تطبيقات متعددة مؤشرات ترابط عند كتابة "مضمنة" دالة / أسلوب؟

لا ، هذا لا يهم على الإطلاق.







one-definition-rule