سؤال

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

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

التعليمات البرمجية كما يلي:

class base
{
public:
    virtual ~base() {}
    virtual uint func(uint i) = 0;
};

class derived : public base
{
public:
    ~derived() {}
    uint func(uint i) { return i * 2; }
};

uint j = 0;
ulong k = 0;
double l = 0;
ushort numIters = 10;
base* mybase = new derived;  // or derived* myderived = ...

for(ushort i = 0; i < numIters; i++)
{
  clock_t start2, finish2;
  start2 = clock();

  for (uint j = 0; j < 100000000; ++j)
        k += mybase->func(j);

  finish2 = clock();
  l += (double) (finish2 - start2);
  std::cout << "Total duration: " << (double) (finish2 - start2) << " ms." << std::endl;

}

std::cout << "Making sure the loop is not optimized to nothing: " << k << std::endl;
std::cout << "Average duration: " << l / numIters << " ms." << std::endl;

النتائج:

base* mybase = new derived; يعطي في المتوسط ~338 ms.

derived* myderived = new derived; يعطي في المتوسط ~338 ms.

القضاء على الميراث وإزالة وظائف افتراضية يعطي متوسط ~38 مللي.

هذا تقريبا 10 مرات أقل!وذلك أساسا, إذا كان أي وظيفة أعلنت الظاهرية في النفقات العامة تكون دائما مماثل الوقت الحاضر ، حتى لو كنت لا تستخدم polymorphically?

شكرا

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

المحلول

الوصول إليه "مباشرة" هو فعل نفس العمل الوصول إليه "بشكل غير مباشر".

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

استدعاء دالة غير polymorphically لا تستخدم مؤشر:

derived myderived;
myderived.func(1); 

عند إزالة وظائف افتراضية, مترجم مضمنة يمكن استدعاء دالة بحيث يمكنك أساسا في نهاية المطاف مع حلقة بسيطة:

for (uint j = 0; j < 100000000; ++j)
    k += i * 2;

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

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

نصائح أخرى

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

طريقة العثور عليها هو قبل التوقف التطبيق عدة مرات تحت المصحح ، ودراسة الدولة ، بما في ذلك استدعاء المكدس. هنا مثال استخدام هذه الطريقة للحصول على 43x تسريع.

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