pha*_*tom 9 c++ performance c++11
throw()在C++ 03中作为异常说明符添加,但在C++ 11中对于说明noexcept符已弃用.
在分析了一些代码以找到使用的速度之后throw(),noexcept我发现所有这些函数的函数调用大致相同.
结果:
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
Run Code Online (Sandbox Code Playgroud)
这是我用来描述的代码:
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;
}
Run Code Online (Sandbox Code Playgroud)
功能定义为:
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();
}
Run Code Online (Sandbox Code Playgroud)
我对这些结果感到惊讶,因为我的印象是,throw()并noexcept为该功能增加了一些开销.
为什么不throw()和noexcept相比于普通函数调用添加任何开销函数调用?
How*_*ant 13
gcc和clang具有非常相似的代码生成,因为它们在很大程度上是ABI彼此兼容的.我只能回答你的问题,但我的回答应该适用于你的gcc编译器.
可以反汇编throw_func,noexcept_func并std_func使用-S命令行标志.这样做,你会注意到这三个函数都生成了非常相似的程序集.
差异包括:
功能的名字将有所不同: __Z10throw_funcv,__Z13noexcept_funcv,和__Z8std_funcv.
除了标签名称之外,所有三个函数的"正常路径"汇编代码都是相同的.
throw_func并将noexcept_func在代码后生成"异常表".这些表指示低级C++运行时库如何展开堆栈.这包括有关必须运行哪些析构函数的指令,要尝试的catch块,以及在这两个函数的情况下,如果异常试图传播出来该怎么办. throw_func将包含一个调用___cxa_call_unexpected,noexcept_func并将包含一个调用___clang_call_terminate(对于将被命名为其他内容的gcc).
std_func不会包含对unexpectednor的调用terminate.它也没有"例外表".没有析构函数可以运行,没有try/catch子句登陆,也没有必要调用unexpected或terminate.
总之,这三个功能的唯一区别在于"特殊路径".而"特殊路径"增加了代码大小,但永远不会被你的代码执行main.在实际代码中,只是添加的代码大小会影响运行时性能.但是对于经常执行且足够小以适应缓存的代码(例如此测试),代码大小命中不会导致任何运行时性能损失.
为了完整性,这是noexcept_func装配.以下所有内容LBB0_1:都是为了处理这条特殊的道路. LBB0_1:通过Ltmp1:抛出异常. Ltmp2:是一个"登陆垫",如果异常试图通过此处解除,运行时将分支到该"登陆垫".并且GCC_except_table0:是异常表本身.
.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 __ZTI9exception@GOTPCREL(%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
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
712 次 |
| 最近记录: |