سؤال

< باكغوند>

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

< /الخلفية>

الآن أنا بحاجة إلى هذه الميزة أن تكون في أسرع وقت ممكن (دون رمز التجميع أو GPU حساب هذا لا يزال يتعين C++ و أكثر قابلية للقراءة أقل).الآن أنا أعرف القليل عن قوالب فئة السياسات (من Alexandrescu هو كتاب ممتاز) و أعتقد أن الترجمة الوقت رمز جيل قد يكون الحل.

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

من الواضح أنا بحاجة الى تحسينات على لأنه بدون هذه g++ (وربما غيرها من المجمعين وكذلك) من شأنها أن تبقي بعض العمليات غير الضرورية في رمز الكائن.أنا أيضا بحاجة إلى جعل الثقيلة استخدام ميزة جديدة في المؤشر بسبب دلتا 1e-3 الثانية يمكن أن تجعل الفرق بين جيدة وسيئة تصميم (هذه الميزة سوف تكون اتصلت مليون مرة في البرنامج الحقيقي).

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

إذا قمت بإضافة بعض الطباعة إلى المعياري ، فإن المترجم ثم يضطر للقيام حساب في الحلقة لكن ربما في الغالب معايير iostream التنفيذ.

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

شكرا على النصائح !


يبدو أن هناك عدة استراتيجيات من مترجم-خيارات محددة مما يتيح ضبط أكثر العامة الحلول التي يجب العمل مع كل مترجم مثل volatile أو extern.

أعتقد أنني سوف تحاول كل من هذه.شكرا جزيلا على كل ما تبذلونه من الأجوبة!

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

المحلول

إذا كنت ترغب في القوة أي مترجم إلى عدم تجاهل لذلك هو يكتب النتيجة إلى متقلبة الكائن.أن العملية لا يمكن أن يكون الأمثل من خلال التعريف.

template<typename T> void sink(T const& t) {
   volatile T sinkhole = t;
}

لا iostream علوية فقط نسخة التي يجب أن تبقى في التعليمات البرمجية التي تم إنشاؤها.الآن, إذا كنت جمع النتائج من الكثير من العمليات ، فمن الأفضل عدم التخلص منهم واحدا تلو الآخر.هذه النسخ لا يزال إضافة بعض النفقات العامة.بدلا من ذلك بطريقة أو بأخرى جمع كل النتائج في واحد غير متغير كائن (لذلك كل النتائج الفردية مطلوبة) ومن ثم تعيين ذلك نتيجة الاعتراض على المتقلبة.E. g.إذا كان الخاص بك الفردية عمليات إنتاج سلاسل, يمكنك قوة التقييم بإضافة كل شار القيم معا مودولو 1<<32.وهذا يضيف أي النفقات العامة ؛ سلاسل من المرجح أن تكون في ذاكرة التخزين المؤقت.نتيجة ذلك سيكون في وقت لاحق أن تسند إلى متقلبة بذلك كل شار في كل لدغة يجب أن تكون في الواقع حساب ولا توجد اختصارات المسموح بها.

نصائح أخرى

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

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

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

رأيي المتواضع هو أن كنت قد تعرضت لبعض المشاكل الأخرى في وقت سابق على مثل الطريقة C/C++ تقييم التعبيرات المنطقية.

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

للإجابة عن سؤالك في المختبر اختبار:نعم فعل ذلك.إذا كان التطبيق الخاص بك هو في الوقت الحرج ، تفعل ذلك.من ناحية أخرى ، وصف تلميحات في مشكلة مختلفة:إذا كان الخاص بك دلتا في إطار زمني من 1e-3 ثوان ، ثم تبدو مشكلة من التعقيد الحسابي, لأن الأسلوب في السؤال يجب أن تسمى جدا جدا في كثير من الأحيان (على عدة أشواط ، 1e-3 ثوان neglectible).

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

من باب الفضول ما هي المشكلة هل هي حساب?

لديك الكثير من السيطرة على تحسينات من أجل تجميع الخاص بك.-O1, O2-وهكذا هي مجرد أسماء مستعارة لمجموعة من المفاتيح.

من الرجل الصفحات

       -O2 turns on all optimization flags specified by -O.  It also turns
       on the following optimization flags: -fthread-jumps -falign-func‐
       tions  -falign-jumps -falign-loops  -falign-labels -fcaller-saves
       -fcrossjumping -fcse-follow-jumps  -fcse-skip-blocks
       -fdelete-null-pointer-checks -fexpensive-optimizations -fgcse
       -fgcse-lm -foptimize-sibling-calls -fpeephole2 -fregmove -fre‐
       order-blocks  -freorder-functions -frerun-cse-after-loop
       -fsched-interblock  -fsched-spec -fschedule-insns  -fsched‐
       ule-insns2 -fstrict-aliasing -fstrict-overflow -ftree-pre
       -ftree-vrp

يمكنك قرص استخدام هذا الأمر أن تساعدك على تضييق الخيارات التي للتحقيق.

       ...
       Alternatively you can discover which binary optimizations are
       enabled by -O3 by using:

               gcc -c -Q -O3 --help=optimizers > /tmp/O3-opts
               gcc -c -Q -O2 --help=optimizers > /tmp/O2-opts
               diff /tmp/O2-opts /tmp/O3-opts Φ grep enabled

عندما تجد culpret الأمثل يجب أن لا تحتاج cout هو.

إذا كان هذا ممكنا بالنسبة لك ، قد حاول تقسيم المدونة إلى:

  • المكتبة كنت ترغب في اختبار جمعتها مع كل التحسينات تشغيل
  • برنامج الاختبار dinamically تربط المكتبة مع تحسينات إيقاف تشغيل

وإلا قد حدد مختلف تحسين مستوى (يبدو انك تستخدم دول مجلس التعاون الخليجي...) لاختبار وظيفة n مع تحسين السمة (انظر http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html#Function-Attributes).

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

#include <iostream>

// Mark coords as extern.
// Compiler is now NOT allowed to optimise away coords
// This it can not remove the loop where you initialise it.
// This is because the code could be used by another compilation unit
extern double coords[500][3];
double coords[500][3];

int main()
{

//perform a simple initialization of all coordinates:
for (int i=0; i<500; ++i)
 {
   coords[i][0] = 3.23;
   coords[i][1] = 1.345;
   coords[i][2] = 123.998;
 }


std::cout << "hello world !"<< std::endl;
return 0;
}

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

StartBenchmarking(); // ie, read a performance counter
for (int i=0; i<500; ++i)
 {
   coords[i][0] = 3.23;
   coords[i][1] = 1.345;
   coords[i][2] = 123.998;
 }
StopBenchmarking(); // what comes after this won't go into the timer

// this is just to force the compiler to use coords
double foo;
for (int j = 0 ; j < 500 ; ++j )
{
  foo += coords[j][0] + coords[j][1] + coords[j][2]; 
}
cout << foo;

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

void test1( volatile double *coords )
{
  //perform a simple initialization of all coordinates:
  for (int i=0; i<1500; i+=3)
  {
    coords[i+0] = 3.23;
    coords[i+1] = 1.345;
    coords[i+2] = 123.998;
  }
}

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

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

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

أنا لا أعرف إذا كان مجلس التعاون الخليجي لديها ميزة مشابهة ، ولكن مع VC++ يمكنك استخدام:

#pragma optimize

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

مجرد مثال صغير من غير المرغوب فيه الأمثل:

#include <vector>
#include <iostream>

using namespace std;

int main()
{
double coords[500][3];

//perform a simple initialization of all coordinates:
for (int i=0; i<500; ++i)
 {
   coords[i][0] = 3.23;
   coords[i][1] = 1.345;
   coords[i][2] = 123.998;
 }


cout << "hello world !"<< endl;
return 0;
}

إذا كنت التعليق رمز من "ضعف coords[500][3]" إلى نهاية حلقة هذا سوف تولد بالضبط نفس رمز التجميع (فقط حاولت مع g++ 4.3.2).أعلم أن هذا المثال هو بسيط جدا, و أنا لم أكن قادرا على إظهار هذا السلوك مع std::ناقل بسيط "الإحداثيات" هيكل.

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

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

عند بدء التشغيل, قراءة من ملف.في التعليمات البرمجية الخاصة بك ، أقول إذا(المدخلات == "x") cout<< result_of_benchmark;

المترجم لن تكون قادرة على القضاء على الحساب ، و إذا كان عليك التأكد من المدخلات ليست "x" لن القياسي في iostream.

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