我猜对了,但仍然感到惊讶的是,这两个程序的输出,用C和C ++编写,在编译时非常不同。 这让我觉得对象的概念仍然存在于最低层。 这会增加开销吗?如果是这样,目前将面向对象的代码转换为程序样式或者很难做到这一点是不可能的优化吗?
#include <stdio.h>
int main(void) {
printf("Hello World!\n");
return 0;
}
#include <iostream>
int main() {
std::cout << "Hello World!" << std::endl;
return 0;
}
编译如下:
gcc helloworld.cpp -o hwcpp.S -S -O2
gcc helloworld.c -o hwc.S -S -O2
制作此代码:
.file "helloworld.c"
.section .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "Hello World!\n"
.text
.p2align 4,,15
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $16, %esp
movl $.LC0, 4(%esp)
movl $1, (%esp)
call __printf_chk
xorl %eax, %eax
leave
ret
.size main, .-main
.ident "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
.section .note.GNU-stack,"",@progbits
.file "helloworld.cpp"
.text
.p2align 4,,15
.type _GLOBAL__I_main, @function
_GLOBAL__I_main:
.LFB1007:
.cfi_startproc
.cfi_personality 0x0,__gxx_personality_v0
pushl %ebp
.cfi_def_cfa_offset 8
movl %esp, %ebp
.cfi_offset 5, -8
.cfi_def_cfa_register 5
subl $24, %esp
movl $_ZStL8__ioinit, (%esp)
call _ZNSt8ios_base4InitC1Ev
movl $__dso_handle, 8(%esp)
movl $_ZStL8__ioinit, 4(%esp)
movl $_ZNSt8ios_base4InitD1Ev, (%esp)
call __cxa_atexit
leave
ret
.cfi_endproc
.LFE1007:
.size _GLOBAL__I_main, .-_GLOBAL__I_main
.section .ctors,"aw",@progbits
.align 4
.long _GLOBAL__I_main
.section .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "Hello World!"
.text
.p2align 4,,15
.globl main
.type main, @function
main:
.LFB997:
.cfi_startproc
.cfi_personality 0x0,__gxx_personality_v0
pushl %ebp
.cfi_def_cfa_offset 8
movl %esp, %ebp
.cfi_offset 5, -8
.cfi_def_cfa_register 5
andl $-16, %esp
pushl %ebx
subl $28, %esp
movl $12, 8(%esp)
movl $.LC0, 4(%esp)
movl $_ZSt4cout, (%esp)
.cfi_escape 0x10,0x3,0x7,0x55,0x9,0xf0,0x1a,0x9,0xfc,0x22
call _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_i
movl _ZSt4cout, %eax
movl -12(%eax), %eax
movl _ZSt4cout+124(%eax), %ebx
testl %ebx, %ebx
je .L9
cmpb $0, 28(%ebx)
je .L5
movzbl 39(%ebx), %eax
.L6:
movsbl %al,%eax
movl %eax, 4(%esp)
movl $_ZSt4cout, (%esp)
call _ZNSo3putEc
movl %eax, (%esp)
call _ZNSo5flushEv
addl $28, %esp
xorl %eax, %eax
popl %ebx
movl %ebp, %esp
popl %ebp
ret
.p2align 4,,7
.p2align 3
.L5:
movl %ebx, (%esp)
call _ZNKSt5ctypeIcE13_M_widen_initEv
movl (%ebx), %eax
movl $10, 4(%esp)
movl %ebx, (%esp)
call *24(%eax)
jmp .L6
.L9:
call _ZSt16__throw_bad_castv
.cfi_endproc
.LFE997:
.size main, .-main
.local _ZStL8__ioinit
.comm _ZStL8__ioinit,1,1
.weakref _ZL20__gthrw_pthread_oncePiPFvvE,pthread_once
.weakref _ZL27__gthrw_pthread_getspecificj,pthread_getspecific
.weakref _ZL27__gthrw_pthread_setspecificjPKv,pthread_setspecific
.weakref _ZL22__gthrw_pthread_createPmPK14pthread_attr_tPFPvS3_ES3_,pthread_create
.weakref _ZL20__gthrw_pthread_joinmPPv,pthread_join
.weakref _ZL21__gthrw_pthread_equalmm,pthread_equal
.weakref _ZL20__gthrw_pthread_selfv,pthread_self
.weakref _ZL22__gthrw_pthread_detachm,pthread_detach
.weakref _ZL22__gthrw_pthread_cancelm,pthread_cancel
.weakref _ZL19__gthrw_sched_yieldv,sched_yield
.weakref _ZL26__gthrw_pthread_mutex_lockP15pthread_mutex_t,pthread_mutex_lock
.weakref _ZL29__gthrw_pthread_mutex_trylockP15pthread_mutex_t,pthread_mutex_trylock
.weakref _ZL31__gthrw_pthread_mutex_timedlockP15pthread_mutex_tPK8timespec,pthread_mutex_timedlock
.weakref _ZL28__gthrw_pthread_mutex_unlockP15pthread_mutex_t,pthread_mutex_unlock
.weakref _ZL26__gthrw_pthread_mutex_initP15pthread_mutex_tPK19pthread_mutexattr_t,pthread_mutex_init
.weakref _ZL29__gthrw_pthread_mutex_destroyP15pthread_mutex_t,pthread_mutex_destroy
.weakref _ZL30__gthrw_pthread_cond_broadcastP14pthread_cond_t,pthread_cond_broadcast
.weakref _ZL27__gthrw_pthread_cond_signalP14pthread_cond_t,pthread_cond_signal
.weakref _ZL25__gthrw_pthread_cond_waitP14pthread_cond_tP15pthread_mutex_t,pthread_cond_wait
.weakref _ZL30__gthrw_pthread_cond_timedwaitP14pthread_cond_tP15pthread_mutex_tPK8timespec,pthread_cond_timedwait
.weakref _ZL28__gthrw_pthread_cond_destroyP14pthread_cond_t,pthread_cond_destroy
.weakref _ZL26__gthrw_pthread_key_createPjPFvPvE,pthread_key_create
.weakref _ZL26__gthrw_pthread_key_deletej,pthread_key_delete
.weakref _ZL30__gthrw_pthread_mutexattr_initP19pthread_mutexattr_t,pthread_mutexattr_init
.weakref _ZL33__gthrw_pthread_mutexattr_settypeP19pthread_mutexattr_ti,pthread_mutexattr_settype
.weakref _ZL33__gthrw_pthread_mutexattr_destroyP19pthread_mutexattr_t,pthread_mutexattr_destroy
.ident "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
.section .note.GNU-stack,"",@progbits
答案 0 :(得分:19)
不同的编译器生成不同的代码。 gcc的早期版本与当前版本的gcc可能产生不同的代码。
更重要的是,iostream
处理很多事情stdio
没有处理,因此显然会有一些实质性的开销。我理解,从理论上讲,这些可以编译成缩进的代码,但他们所做的并不是技术上相同的。
答案 1 :(得分:8)
这里的问题不是关于对象或优化:printf
和cout
基本上是非常不同的野兽。要进行更公平的比较,请使用cout
替换C ++代码中的printf
语句。当你输出到stdout时,优化是一个没有实际意义的点,因为瓶颈肯定是终端的缓冲区。
答案 2 :(得分:6)
您没有像C示例那样在C ++示例中调用相同的函数。用简单的旧printf替换std :: cout管道,就像C代码一样,你应该看到两个编译器的输出之间有更大的相关性。
答案 3 :(得分:2)
你必须意识到C ++中还有很多“其他”的东西。 例如全局构造函数。图书馆也不同。
C ++流对象比C io复杂得多,如果查看汇编程序,可以看到C ++版本中pthreads的所有代码。
它不一定慢,但肯定不同。
答案 4 :(得分:2)
我猜对了,但仍然感到惊讶的是,这两个程序的输出,用C和C ++编写,在编译时非常不同。
我很惊讶你很惊讶 - 他们的节目完全不同。
这让我觉得对象的概念在最低层仍然存在。
绝对......对象 是在程序执行期间布局和使用内存的方式(需要进行优化)。
这会增加开销吗?
不一定或通常 - 如果以相同的逻辑方式协调工作,相同的数据无论如何都必须在某处。
如果是这样,目前将面向对象的代码转换为程序样式或者很难做到这一点是不可能的优化?
该问题与OO与程序代码无关,或者与其他代码相比更有效。你在这里观察到的主要问题是C ++的ostreams需要更多的设置和拆卸,并且更多的I / O由内联代码协调,而printf()在预编译库中有更多的外联线,所以你在你的小代码清单中看不到它。目前尚不清楚哪个“更好”,除非你有性能问题,分析显示是相关的,你应该忘记它并完成一些有用的编程。
编辑回应评论:
公平的电话 - 措辞有点严厉 - 抱歉。这实际上是一个难以区分的......“只有编译器[知道]对象”在某种意义上是正确的 - 它们不是封装的,半神圣离散的“事物”,它们可以与程序员一样。而且,我们可以编写一个可以像使用cout
一样使用的对象,它会在编译期间消失,并生成与printf()版本相同的代码。但是,cout
和iostreams涉及一些设置,因为它是线程安全的,更内联并处理不同的语言环境,它是一个具有存储要求的真实对象,因为它带有更多关于错误状态的独立信息,无论你是否想要抛出异常,结束-of-file条件(printf()影响“errno”,然后被下一个库/ OS调用破坏)....
您可能会发现更有见地的是比较在打印一个字符串时生成多少额外代码,因为代码量基本上是一些常量开销+一些每次使用量,后者{{1}基于代码的类型和格式,可以比printf()更高效或更高效。值得注意的是......
ostream
...是正确的,更类似于你的printf()语句... std::cout << "Hello world!\n";
显式请求不必要的刷新操作,因为符合标准的C ++程序将在流消失时刷新并关闭其缓冲区范围无论如何(也就是说,今天有一个有趣的帖子,似乎有人的Microsoft VisualC ++编译器没有为他们做这些! - 值得关注但很难相信)。