2015-04-29 15 views
9

throw() được thêm vào C++ 03 làm trình chỉ định ngoại lệ, tuy nhiên nó không được dùng trong C++ 11 cho thông số noexcept.Tại sao không ném() và không chấp nhận có bất kỳ chi phí nào?

Sau khi lược tả một số mã để tìm tốc độ sử dụng throw(), noexcept và chỉ các hàm thuần túy tôi thấy rằng tất cả chúng đều có cùng thời gian cho lệnh gọi hàm.

Kết quả:

throw()  noexcept  plain old function 
11233 ms  11105 ms  11216 ms 
11195 ms  11122 ms  11150 ms 
11192 ms  11151 ms  11231 ms 
11214 ms  11218 ms  11228 ms 

compiled with MinGW using g++ -o test.exe inc.cpp no.cpp -std=c++11 -O3 

Đây là mã tôi đã sử dụng để cấu hình:

int main() { 
    unsigned long long iter = (unsigned long long)1 << (unsigned long long)40; 
    auto t1 = std::chrono::high_resolution_clock::now(); 

    for(unsigned long long i = 0; i < iter; i++) 
    { 
#ifdef THROW 
     throw_func(); 
#elif defined NOEXCEPT 
     noexcept_func(); 
#else 
     std_func(); 
#endif 
    } 

    auto t2 = std::chrono::high_resolution_clock::now(); 

    std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count() << " ms" << std::endl; 
} 

Các chức năng được định nghĩa là:

unsigned long long val = 1; 

struct exception { }; 

void throw_func(void) throw() 
{ 
    if (!val++) 
     throw exception(); 
} 
void noexcept_func(void) noexcept 
{ 
    if (!val++) 
     throw exception(); 
} 
void std_func(void) 
{ 
    if (!val++) 
     throw exception(); 
} 

Tôi đã rất ngạc nhiên bởi những kết quả này bởi vì tôi theo ấn tượng rằng throw()noexcept đã thêm một số phí vào không còn tồn tại ion.

Tại sao không throw()noexcept thêm bất kỳ phí nào vào cuộc gọi hàm so với cuộc gọi chức năng thông thường?

+2

Câu lệnh đúng sẽ gần hơn với 'throw()' và chủ yếu là 'noexcept' không loại bỏ nhiều chi phí trong trường hợp chung. Trong khi trình biên dịch có vẻ như nó có thể tránh xử lý ngoại lệ cho hàm đó, thực tế là nó phải gọi chấm dứt nếu một ngoại lệ được ném có nghĩa là nó không thể thực sự. –

+0

@ DavidRodríguez-dribeas Vì vậy, trường hợp 'noexcept' vẫn đang thực hiện kiểm tra ngoại lệ đầy đủ để ngăn chặn các ngoại lệ thoát khỏi chức năng? – phantom

+1

@ DavidRodríguez-dribeas Đối với 'noexcept', không cần phải thư giãn ngăn xếp giúp. –

Trả lời

13

gcc và clang có quá trình tạo mã rất giống nhau vì chúng phần lớn là ABI tương thích với nhau. Tôi chỉ có clang để trả lời câu hỏi của bạn, nhưng câu trả lời của tôi nên áp dụng cho trình biên dịch gcc của bạn khá chặt chẽ.

Có thể tháo rời throw_func, noexcept_funcstd_func bằng cờ dòng lệnh -S. Khi làm như vậy, bạn sẽ lưu ý rằng cả ba chức năng đều tạo ra sự lắp ráp tương tự đáng kể.

khác biệt bao gồm:

  1. Các tên đọc sai các chức năng sẽ khác nhau: __Z10throw_funcv, __Z13noexcept_funcv, và __Z8std_funcv.

  2. Mã lắp ráp "đường dẫn bình thường" sẽ giống hệt nhau cho cả ba chức năng ngoại trừ có lẽ cho tên nhãn.

  3. throw_funcnoexcept_func sẽ tạo "bảng ngoại lệ" sau mã. Các bảng này hướng dẫn thư viện thời gian chạy C++ ở mức thấp như thế nào để thư giãn ngăn xếp. Điều này bao gồm các hướng dẫn về những gì destructors phải được chạy, những gì bắt khối để thử, và trong trường hợp của hai chức năng, phải làm gì nếu một ngoại lệ cố gắng tuyên truyền ra ngoài. throw_func sẽ chứa một cuộc gọi đến ___cxa_call_unexpectednoexcept_func sẽ chứa một cuộc gọi đến ___clang_call_terminate (đối với gcc sẽ được đặt tên khác).

  4. std_func sẽ không chứa các cuộc gọi đến unexpected cũng không terminate. Nó cũng sẽ không có một "bảng ngoại lệ". Không có destructors để chạy, không có cố gắng/bắt điều khoản để hạ cánh, và không cần phải gọi unexpected hoặc terminate.

Tóm lại, sự khác biệt duy nhất trong ba hàm này là trong "đường dẫn ngoại lệ". Và "đường dẫn đặc biệt" thêm kích thước mã, nhưng không bao giờ được thực thi bởi main của bạn.Trong mã thực tế, chỉ kích thước mã được thêm có thể tác động đến thời gian chạy tác động. Tuy nhiên đối với mã được thực hiện thường xuyên và đủ nhỏ để vừa với bộ nhớ cache (chẳng hạn như thử nghiệm này), lần truy cập kích thước mã sẽ không gây ra bất kỳ lần truy cập hiệu suất thời gian chạy nào.

Để hoàn thành, đây là hội đồng noexcept_func. Mọi thứ bên dưới LBB0_1: là để xử lý đường dẫn đặc biệt. LBB0_1: thông qua Ltmp1: ném ngoại lệ. Ltmp2: là "pad đích" mà thời gian chạy sẽ phân nhánh nếu ngoại lệ cố gắng thư giãn thông qua đây. Và GCC_except_table0: là bảng ngoại lệ.

.globl __Z13noexcept_funcv 
    .align 4, 0x90 
__Z13noexcept_funcv:     ## @_Z13noexcept_funcv 
    .cfi_startproc 
    .cfi_personality 155, ___gxx_personality_v0 
Leh_func_begin0: 
    .cfi_lsda 16, Lexception0 
## BB#0: 
    pushq %rbp 
Ltmp3: 
    .cfi_def_cfa_offset 16 
Ltmp4: 
    .cfi_offset %rbp, -16 
    movq %rsp, %rbp 
Ltmp5: 
    .cfi_def_cfa_register %rbp 
    movq _val(%rip), %rax 
    leaq 1(%rax), %rcx 
    movq %rcx, _val(%rip) 
    testq %rax, %rax 
    je LBB0_1 
LBB0_2: 
    popq %rbp 
    retq 
LBB0_1: 
    movl $1, %edi 
    callq ___cxa_allocate_exception 
Ltmp0: 
    movq [email protected](%rip), %rsi 
    xorl %edx, %edx 
    movq %rax, %rdi 
    callq ___cxa_throw 
Ltmp1: 
    jmp LBB0_2 
LBB0_3: 
Ltmp2: 
    movq %rax, %rdi 
    callq ___clang_call_terminate 
    .cfi_endproc 
Leh_func_end0: 
    .section __TEXT,__gcc_except_tab 
    .align 2 
GCC_except_table0: 
Lexception0: 
    .byte 255      ## @LPStart Encoding = omit 
    .byte 155      ## @TType Encoding = indirect pcrel sdata4 
    .asciz "\242\200\200"   ## @TType base offset 
    .byte 3      ## Call site Encoding = udata4 
    .byte 26      ## Call site table length 
Lset0 = Leh_func_begin0-Leh_func_begin0 ## >> Call Site 1 << 
    .long Lset0 
Lset1 = Ltmp0-Leh_func_begin0   ## Call between Leh_func_begin0 and Ltmp0 
    .long Lset1 
    .long 0      ##  has no landing pad 
    .byte 0      ## On action: cleanup 
Lset2 = Ltmp0-Leh_func_begin0   ## >> Call Site 2 << 
    .long Lset2 
Lset3 = Ltmp1-Ltmp0      ## Call between Ltmp0 and Ltmp1 
    .long Lset3 
Lset4 = Ltmp2-Leh_func_begin0   ##  jumps to Ltmp2 
    .long Lset4 
    .byte 1      ## On action: 1 
    .byte 1      ## >> Action Record 1 << 
             ## Catch TypeInfo 1 
    .byte 0      ## No further actions 
             ## >> Catch TypeInfos << 
    .long 0      ## TypeInfo 1 
    .align 2 
+3

Để tóm tắt: sự khác biệt tồn tại trong các đường dẫn mã không thể truy cập được trong trường hợp thử nghiệm đã cho, do đó thời gian chạy là thống kê giống hệt nhau – MSalters

Các vấn đề liên quan