سؤال

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

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

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

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

المحلول

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

أيضا ، من المحتمل أن تكون المتغيرات المحلية أسرع بسبب موقع ذاكرة التخزين المؤقت.

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


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

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

نصائح أخرى

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

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

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

في لغة مختلفة، C++، يدعم المعيار الجديد إعادة التوجيه المثالي، ودلالات النقل المثالية مع مراجع rvalue التي تلغي الحاجة إلى مؤقتات في حالات معينة والتي يمكن أن تقلل من تكلفة استدعاء دالة.

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

تماما لا! الفرق الوحيد "الأداء" هو عند تهيئة المتغيرات

    int anint = 42;
 vs
    static int anint = 42;

في الحالة الأولى ، سيتم ضبط عدد صحيح على 42 في كل مرة يتم فيها استدعاء الوظيفة في الحالة الثانية ، سيتم تعيين OT على 42 عند تحميل البرنامج.

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

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

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

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

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

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

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

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

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

هذا مضاعف عند تقديم مؤشرات ، قل أن لديك الرمز التالي:

int myFunction()
{
    SomeStruct *A, *B;
    FillOutSomeStruct(B);
    memcpy(A, B, sizeof(A);
    return A.result;
}

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

http://en.wikipedia.org/wiki/pointer_alias

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

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

كاقتراحات M2TM ، فإن تشغيل البروفيلر هو أيضًا فكرة جيدة. الدفع GPROF لواحد سهل الاستخدام.

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

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

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

الفرق الرئيسي بين السيناريوهات هو بصمة الذاكرة ، وليس السرعة الكبيرة.

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

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

قد لا يرى التنميط الفرق، والتفكيك ومعرفة ما الذي تبحث عنه قد يكون.

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

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

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

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top