[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参数吗
|