为何引入析构函数会导致代码生成劣化?(寄存器传值变传引用)
空析构函数导致代码生成差异的问题
示例代码
struct has_destruct_t { int a; ~has_destruct_t() {} }; struct no_destruct_t { int a; }; int bar_no_destruct(no_destruct_t); int foo_no_destruct(void) { no_destruct_t tmp{}; bar_no_destruct(tmp); return 0; } int bar_has_destruct(has_destruct_t); int foo_has_destruct(void) { has_destruct_t tmp{}; bar_has_destruct(tmp); return 0; }
代码生成差异
foo_has_destruct的代码生成质量稍差,因为析构函数似乎迫使tmp存入栈中:
foo_no_destruct(): # @foo_no_destruct() pushq %rax xorl %edi, %edi callq bar_no_destruct(no_destruct_t)@PLT xorl %eax, %eax popq %rcx retq foo_has_destruct(): # @foo_has_destruct() pushq %rax movl $0, 4(%rsp) leaq 4(%rsp), %rdi callq bar_has_destruct(has_destruct_t)@PLT xorl %eax, %eax popq %rcx retq
问题
既然该析构函数1)可平凡内联且2)为空,为何仍会出现这种情况?是否存在零成本添加析构函数的方法?
解答
手动声明空析构函数会让结构体失去平凡可析构的属性——C++标准规定,只有未声明析构函数,或者析构函数被显式默认(= default)的类型,才属于平凡可析构类型。平凡可析构类型的对象在生命周期结束时不需要执行任何析构操作,编译器因此拥有更大的优化空间,比如直接在寄存器中构造参数并传递,无需将对象存入栈中。
要实现零成本添加析构函数,只需将空析构函数替换为显式默认的版本:
struct has_destruct_t { int a; ~has_destruct_t() = default; };
这样结构体依然保持平凡可析构特性,编译器会生成与foo_no_destruct一致的优化代码,既保留了析构函数的声明,又不会带来额外的性能开销。
内容的提问来源于stack exchange,提问作者Noah
相关产品推荐
相关产品推荐

