[Template及应用] 关于std::string的编译

dynamo2 2007-07-13
问题:std::string编译后产生大量汇编代码导致可执行文件过大。
程序上下下文:
    declare: fun(const string& s);
    caller: fun("test");
编译环境:
    GCC 2.9
结果:
    一条简单的函数调用“fun("test")”被翻译成至少200行汇编代码。
    用GCC -O -S -o *.S *.cpp命令查看的编译结果。
有谁知道这是为什么吗?
qiezi 2007-07-15
fun("test")调用包括一个隐式的string对象构造,主要开销应该就是这点吧,但只是个函数调用,没几条汇编的,后面有些不是string生成的,只有几条string析构调用,这个是C++里面免不了的。另外还有一些gcc插入的指令,还有异常相关的,静态数据。另外编译器也有影响,我用的gcc 3.4就没这么多,加上这么多标签也才145行。

    .file   "teststr.cpp"
    .weakref    _Z20__gthrw_pthread_oncePiPFvvE,pthread_once
    .weakref    _Z26__gthrw_pthread_key_createPjPFvPvE,pthread_key_create
    .weakref    _Z26__gthrw_pthread_key_deletej,pthread_key_delete
    .weakref    _Z27__gthrw_pthread_getspecificj,pthread_getspecific
    .weakref    _Z27__gthrw_pthread_setspecificjPKv,pthread_setspecific
    .weakref    _Z22__gthrw_pthread_createPmPK16__pthread_attr_sPFPvS3_ES3_,pthread_create
    .weakref    _Z26__gthrw_pthread_mutex_lockP15pthread_mutex_t,pthread_mutex_lock
    .weakref    _Z29__gthrw_pthread_mutex_trylockP15pthread_mutex_t,pthread_mutex_trylock
    .weakref    _Z28__gthrw_pthread_mutex_unlockP15pthread_mutex_t,pthread_mutex_unlock
    .weakref    _Z30__gthrw_pthread_mutexattr_initP19pthread_mutexattr_t,pthread_mutexattr_init
    .weakref    _Z33__gthrw_pthread_mutexattr_settypeP19pthread_mutexattr_ti,pthread_mutexattr_settype
    .weakref    _Z33__gthrw_pthread_mutexattr_destroyP19pthread_mutexattr_t,pthread_mutexattr_destroy
    .weakref    _Z26__gthrw_pthread_mutex_initP15pthread_mutex_tPK19pthread_mutexattr_t,pthread_mutex_init
    .text
    .align 2
.globl _Z3fooRKSs
    .type   _Z3fooRKSs, @function
_Z3fooRKSs:
.LFB913:    
    movq    (%rdi), %rax
    movq    -24(%rax), %rax
    ret
.LFE913:    
    .size   _Z3fooRKSs, .-_Z3fooRKSs
.globl _Unwind_Resume
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string "abc"
    .text   
    .align 2
.globl main 
    .type   main, @function
main:
.LFB917:
    movq    %rbx, -16(%rsp)
.LCFI0:
    movq    %rbp, -8(%rsp)
.LCFI1:
    subq    $56, %rsp
.LCFI2:
    movq    %rsp, %rdi
    call    _ZNSaIcEC1Ev
    leaq    16(%rsp), %rbp
    movq    %rsp, %rdx
    movl    $.LC0, %esi
    movq    %rbp, %rdi
.LEHB0:
    call    _ZNSsC1EPKcRKSaIcE
    movq    %rbp, %rdi
    call    _Z3fooRKSs
    movl    %eax, %ebx
    movq    %rbp, %rdi
    call    _ZNSsD1Ev
.LEHE0:
    jmp .L13
.L6:
.L12:
.L8:
    movq    %rax, %rbx
    movq    %rsp, %rdi
    call    _ZNSaIcED1Ev
.L10:
    movq    %rbx, %rdi
.LEHB1:
    call    _Unwind_Resume
.LEHE1:
.L13:
    movq    %rsp, %rdi
    call    _ZNSaIcED1Ev
    movl    %ebx, %eax
    movq    40(%rsp), %rbx
    movq    48(%rsp), %rbp
    addq    $56, %rsp
    ret
.LFE917:
    .size   main, .-main
    .section    .gcc_except_table,"a",@progbits
.LLSDA917:
    .byte   0xff
    .byte   0xff
    .byte   0x1
    .uleb128 .LLSDACSE917-.LLSDACSB917
.LLSDACSB917:
    .uleb128 .LEHB0-.LFB917
    .uleb128 .LEHE0-.LEHB0
    .uleb128 .L12-.LFB917
    .uleb128 0x0
    .uleb128 .LEHB1-.LFB917
    .uleb128 .LEHE1-.LEHB1
    .uleb128 0x0
    .uleb128 0x0
.LLSDACSE917:
    .text
    .section    .eh_frame,"a",@progbits
.Lframe1:
    .long   .LECIE1-.LSCIE1
.LSCIE1:
    .long   0x0
    .byte   0x1
    .string "zPL"
    .uleb128 0x1
    .sleb128 -8
    .byte   0x10
    .uleb128 0xa
    .byte   0x0
    .quad   __gxx_personality_v0
    .byte   0x0
    .byte   0xc
    .uleb128 0x7
    .uleb128 0x8
    .byte   0x90
    .uleb128 0x1
    .align 8
.LECIE1:
.LSFDE1:
    .long   .LEFDE1-.LASFDE1
.LASFDE1:
    .long   .LASFDE1-.Lframe1
    .quad   .LFB913
    .quad   .LFE913-.LFB913
    .uleb128 0x8
    .quad   0x0
    .align 8
.LEFDE1:
.LSFDE3:
    .long   .LEFDE3-.LASFDE3
.LASFDE3:
    .long   .LASFDE3-.Lframe1
    .quad   .LFB917
    .quad   .LFE917-.LFB917
    .uleb128 0x8
    .quad   .LLSDA917
    .byte   0x4
    .long   .LCFI2-.LFB917
    .byte   0xe
    .uleb128 0x40
    .byte   0x86
    .uleb128 0x2
    .byte   0x83
    .uleb128 0x3
    .align 8
.LEFDE3:
    .section    .note.GNU-stack,"",@progbits
    .ident  "GCC: (GNU) 3.4.6 20060404 (Red Hat 3.4.6-3)"

汇编底子不好,有些地方看不明白。
dynamo2 2007-07-15
谢谢,你的回复。

一个简单的call会变成100多条汇编感觉是比较恐怖的,以前从没有在意过这个问题,直到客户提出目标代码太大才仔细去研究,然后给把以“const string&”为参数的API都又加了一个以“const char*”为参数的API,可执行代码明显变小。

fun(const char* a) {
    string b(a);
    fun(b);
}

所以个人觉得在API中是应该尽量使用"const char*"来作为函数的参数(一定要是const),如果在函数里面需要用string可以显示的声明一个string变量,这样代码的开销会小很多。
qiezi 2007-07-15
string是模板类,代码是在使用的地方实例化的,这也是它能够优化效率的原因。你上面这个测试我怎么感觉没什么道理呢?不应该是这个结果。另外你看到的体积变小并不能说明执行效率提高,string对象构造免不了的,你这样反而增加了输出的大小,除非是string在函数中没有使用被内联优化掉了。
dynamo2 2007-07-17
用这种方法从编译生成的汇编看
   1. 用fun("test")调用只会被翻译成几行汇编代码
   2. 在fun(const char×)中回看到string b(a)会被翻译成几十行汇编代码。

目标代码的大小是减少了,执行效率也许不会提高很多,因为理论上说只是把string的构造过程移到函数里面且通过显示构造方式代替隐式的函数参数类型转换,所以效率肯定不会变低。

string是内联的,使用了-O编译选项。
netpcc 2007-07-20
从const char *到const string &效率确实比较低。至少在构造中会发生内存分配和复制,在析构时要回收内存。
但是如果程序里完全都使用string的话,对fun(const string &)的调用就没什么额外开销了。
pi1ot 2007-08-12
obj文件过大?试过-s参数吗
Global site tag (gtag.js) - Google Analytics