سؤال

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

ما أعرف الاستثناءات هي نفس الشيء كما فعل حفنة من العودة ولكن فإنه يتحقق أيضا عندما يحتاج إلى التوقف عن القيام عودة.كيف تحقق عند القيام التوقف ؟ أنا أخمن وأقول هناك الثانية المكدس الذي يحمل نوع من الاستثناء وكومة الموقع ثم لا يعود حتى يحصل هناك.أنا أيضا أظن أن المرة الوحيدة التي المكدس هو اللمس على رمي كل try/catch.AFAICT تنفيذ سلوك مماثل مع رمز الإرجاع أن تأخذ نفس المقدار من الوقت.لكن كل هذا تخمين ، لذلك أريد أن أعرف.

كيف الاستثناءات العمل حقا ؟

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

المحلول

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

class MyException
{
public:
    MyException() { }
    ~MyException() { }
};

void my_throwing_function(bool throwit)
{
    if (throwit)
        throw MyException();
}

void another_function();
void log(unsigned count);

void my_catching_function()
{
    log(0);
    try
    {
        log(1);
        another_function();
        log(2);
    }
    catch (const MyException& e)
    {
        log(3);
    }
    log(4);
}

أنا جمعت مع g++ -m32 -W -Wall -O3 -save-temps -c, و نظرت إلى إنشاء ملف التجميع.

    .file   "foo.cpp"
    .section    .text._ZN11MyExceptionD1Ev,"axG",@progbits,_ZN11MyExceptionD1Ev,comdat
    .align 2
    .p2align 4,,15
    .weak   _ZN11MyExceptionD1Ev
    .type   _ZN11MyExceptionD1Ev, @function
_ZN11MyExceptionD1Ev:
.LFB7:
    pushl   %ebp
.LCFI0:
    movl    %esp, %ebp
.LCFI1:
    popl    %ebp
    ret
.LFE7:
    .size   _ZN11MyExceptionD1Ev, .-_ZN11MyExceptionD1Ev

_ZN11MyExceptionD1Ev هو MyException::~MyException(), لذا مترجم قررت انها في حاجة إلى غير مضمنة نسخة من المدمر.

.globl __gxx_personality_v0
.globl _Unwind_Resume
    .text
    .align 2
    .p2align 4,,15
.globl _Z20my_catching_functionv
    .type   _Z20my_catching_functionv, @function
_Z20my_catching_functionv:
.LFB9:
    pushl   %ebp
.LCFI2:
    movl    %esp, %ebp
.LCFI3:
    pushl   %ebx
.LCFI4:
    subl    $20, %esp
.LCFI5:
    movl    $0, (%esp)
.LEHB0:
    call    _Z3logj
.LEHE0:
    movl    $1, (%esp)
.LEHB1:
    call    _Z3logj
    call    _Z16another_functionv
    movl    $2, (%esp)
    call    _Z3logj
.LEHE1:
.L5:
    movl    $4, (%esp)
.LEHB2:
    call    _Z3logj
    addl    $20, %esp
    popl    %ebx
    popl    %ebp
    ret
.L12:
    subl    $1, %edx
    movl    %eax, %ebx
    je  .L16
.L14:
    movl    %ebx, (%esp)
    call    _Unwind_Resume
.LEHE2:
.L16:
.L6:
    movl    %eax, (%esp)
    call    __cxa_begin_catch
    movl    $3, (%esp)
.LEHB3:
    call    _Z3logj
.LEHE3:
    call    __cxa_end_catch
    .p2align 4,,3
    jmp .L5
.L11:
.L8:
    movl    %eax, %ebx
    .p2align 4,,6
    call    __cxa_end_catch
    .p2align 4,,6
    jmp .L14
.LFE9:
    .size   _Z20my_catching_functionv, .-_Z20my_catching_functionv
    .section    .gcc_except_table,"a",@progbits
    .align 4
.LLSDA9:
    .byte   0xff
    .byte   0x0
    .uleb128 .LLSDATT9-.LLSDATTD9
.LLSDATTD9:
    .byte   0x1
    .uleb128 .LLSDACSE9-.LLSDACSB9
.LLSDACSB9:
    .uleb128 .LEHB0-.LFB9
    .uleb128 .LEHE0-.LEHB0
    .uleb128 0x0
    .uleb128 0x0
    .uleb128 .LEHB1-.LFB9
    .uleb128 .LEHE1-.LEHB1
    .uleb128 .L12-.LFB9
    .uleb128 0x1
    .uleb128 .LEHB2-.LFB9
    .uleb128 .LEHE2-.LEHB2
    .uleb128 0x0
    .uleb128 0x0
    .uleb128 .LEHB3-.LFB9
    .uleb128 .LEHE3-.LEHB3
    .uleb128 .L11-.LFB9
    .uleb128 0x0
.LLSDACSE9:
    .byte   0x1
    .byte   0x0
    .align 4
    .long   _ZTI11MyException
.LLSDATT9:

مفاجأة!لا توجد تعليمات إضافية على الإطلاق على القانون العادي المسار.المترجم بدلا ولدت إضافية خارج خط الإصلاح كتل التعليمات البرمجية المشار إليها عبر طاولة في نهاية الدالة (الذي هو في الواقع وضع في قسم منفصل من قابل للتنفيذ).ويتم كل هذا العمل وراء الكواليس من قبل المكتبة القياسية ، بناء على هذه الجداول (_ZTI11MyException هو typeinfo for MyException).

حسنا, هذا لم يكن في الواقع مفاجأة بالنسبة لي, كنت أعرف مسبقا كيف أن هذا المترجم فعل ذلك.المستمر مع الجمعية الإخراج:

    .text
    .align 2
    .p2align 4,,15
.globl _Z20my_throwing_functionb
    .type   _Z20my_throwing_functionb, @function
_Z20my_throwing_functionb:
.LFB8:
    pushl   %ebp
.LCFI6:
    movl    %esp, %ebp
.LCFI7:
    subl    $24, %esp
.LCFI8:
    cmpb    $0, 8(%ebp)
    jne .L21
    leave
    ret
.L21:
    movl    $1, (%esp)
    call    __cxa_allocate_exception
    movl    $_ZN11MyExceptionD1Ev, 8(%esp)
    movl    $_ZTI11MyException, 4(%esp)
    movl    %eax, (%esp)
    call    __cxa_throw
.LFE8:
    .size   _Z20my_throwing_functionb, .-_Z20my_throwing_functionb

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

  • المشي المكدس مع مساعدة من الاستثناء الجداول حتى يجدها معالج هذا الاستثناء.
  • الاسترخاء المكدس حتى يحصل على هذا المعالج.
  • فعلا استدعاء معالج.

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

لإنهاء بقية الجمعية الملف:

    .weak   _ZTI11MyException
    .section    .rodata._ZTI11MyException,"aG",@progbits,_ZTI11MyException,comdat
    .align 4
    .type   _ZTI11MyException, @object
    .size   _ZTI11MyException, 8
_ZTI11MyException:
    .long   _ZTVN10__cxxabiv117__class_type_infoE+8
    .long   _ZTS11MyException
    .weak   _ZTS11MyException
    .section    .rodata._ZTS11MyException,"aG",@progbits,_ZTS11MyException,comdat
    .type   _ZTS11MyException, @object
    .size   _ZTS11MyException, 14
_ZTS11MyException:
    .string "11MyException"

على typeinfo البيانات.

    .section    .eh_frame,"a",@progbits
.Lframe1:
    .long   .LECIE1-.LSCIE1
.LSCIE1:
    .long   0x0
    .byte   0x1
    .string "zPL"
    .uleb128 0x1
    .sleb128 -4
    .byte   0x8
    .uleb128 0x6
    .byte   0x0
    .long   __gxx_personality_v0
    .byte   0x0
    .byte   0xc
    .uleb128 0x4
    .uleb128 0x4
    .byte   0x88
    .uleb128 0x1
    .align 4
.LECIE1:
.LSFDE3:
    .long   .LEFDE3-.LASFDE3
.LASFDE3:
    .long   .LASFDE3-.Lframe1
    .long   .LFB9
    .long   .LFE9-.LFB9
    .uleb128 0x4
    .long   .LLSDA9
    .byte   0x4
    .long   .LCFI2-.LFB9
    .byte   0xe
    .uleb128 0x8
    .byte   0x85
    .uleb128 0x2
    .byte   0x4
    .long   .LCFI3-.LCFI2
    .byte   0xd
    .uleb128 0x5
    .byte   0x4
    .long   .LCFI5-.LCFI3
    .byte   0x83
    .uleb128 0x3
    .align 4
.LEFDE3:
.LSFDE5:
    .long   .LEFDE5-.LASFDE5
.LASFDE5:
    .long   .LASFDE5-.Lframe1
    .long   .LFB8
    .long   .LFE8-.LFB8
    .uleb128 0x4
    .long   0x0
    .byte   0x4
    .long   .LCFI6-.LFB8
    .byte   0xe
    .uleb128 0x8
    .byte   0x85
    .uleb128 0x2
    .byte   0x4
    .long   .LCFI7-.LCFI6
    .byte   0xd
    .uleb128 0x5
    .align 4
.LEFDE5:
    .ident  "GCC: (GNU) 4.1.2 (Ubuntu 4.1.2-0ubuntu4)"
    .section    .note.GNU-stack,"",@progbits

أكثر استثناء التعامل مع الجداول و متنوعة معلومات إضافية.

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

في حال كنت تريد المزيد من المعلومات ، وبخاصة ما كل __cxa_ وظائف راجع مواصفات الأصلي جاءوا من:

نصائح أخرى

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

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

تكلفة معالجة الاستثناء رمز عندما لا استثناءات تستخدم الصفر تقريبا.

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

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

  • لا تضع الأشياء الإضافية في الاستثناء.
  • الصيد const المرجعية.

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

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

وهناك مناقشة لائقة من التفاصيل حول مشروع القانون: كيف مترجم C ++ تنفذ معالجة الاستثناء

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

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

وPietrek بيرني كتب مقالا ممتازة على Win32 والهيكلية معالجة استثناء . بينما هذه المادة مكتوبة أصلا في عام 1997، فإنه لا يزال ساريا اليوم (ولكن بالطبع لا ينطبق إلا على ويندوز).

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

وهناك صديق لي وكتب بعض الشيء كيفية Visual C ++ يعالج الاستثناءات قبل بضع سنوات.

http://www.xyzw.de/c160.html

جميع إجابات جيدة.

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

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

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