لماذا غالبًا ما يتم إيقاف تشغيل برنامج C/C++ في وضع التصحيح؟

StackOverflow https://stackoverflow.com/questions/69250

  •  09-06-2019
  •  | 
  •  

سؤال

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

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

المحلول

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


void foo() {
1:  int i;
2:  for(i = 0; i < 2; )
3:    i++;
4:  return;

في هذا المثال، بدون تحسين، يمكنك خطوة واحدة عبر الكود والضغط على الأسطر 1، 2، 3، 2، 3، 2، 4

مع تشغيل التحسين، قد تحصل على مسار تنفيذ يبدو كما يلي:2، 3، 3، 4 أو حتى 4 فقط!(الوظيفة لا تفعل شيئًا بعد كل شيء ...)

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

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

في حين أن التعليمات البرمجية المُحسّنة وغير المُحسّنة يجب أن تكون مكافئة "وظيفيًا"، إلا أنه في ظل ظروف معينة، سيتغير السلوك.
هنا مثال مبسط:

    int* ptr = 0xdeadbeef;  // some address to memory-mapped I/O device
    *ptr = 0;   // setup hardware device
    while(*ptr == 1) {    // loop until hardware device is done
       // do something
    }

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

  • قد يقوم المترجم بتحسين كتلة while (نبدأ بالقيمة 0، ولن تصبح 1 أبدًا)
  • بدلاً من الوصول إلى الذاكرة، قد يتم نقل الوصول إلى المؤشر إلى السجل->لا يوجد تحديث للإدخال/الإخراج
  • قد يتم تخزين الوصول إلى الذاكرة مؤقتًا (ليس بالضرورة أن يكون ذلك مرتبطًا بتحسين برنامج التحويل البرمجي)

في كل هذه الحالات، سيكون السلوك مختلفًا تمامًا وعلى الأرجح خاطئًا.

نصائح أخرى

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

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

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

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

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

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

يسمح لك تصحيح الأخطاء في الوضع غير المحسّن برؤية كل ما كتبته كما كتبته دون أن يقوم المُحسِّن بإزالة الأشياء أو إعادة ترتيبها.

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

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

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

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

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

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

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

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

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

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

على الأقل في نظام Linux (وليس هناك سبب يجعل نظام Windows مختلفًا) يتم تجميع معلومات تصحيح الأخطاء في قسم منفصل من الملف الثنائي، ولا يتم تحميلها أثناء التنفيذ العادي.ويمكن تقسيمها إلى ملف مختلف لاستخدامه في تصحيح الأخطاء.أيضًا، في بعض المترجمين (بما في ذلك Gcc، أعتقد أيضًا باستخدام مترجم Microsoft C) يمكن تمكين معلومات تصحيح الأخطاء والتحسينات معًا.إذا لم يكن الأمر كذلك، فمن الواضح أن الكود سيكون أبطأ.

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