GCC / g ++ cout<<与printf()

时间:2011-03-06 16:12:41

标签: c++ c gcc assembly g++

  • 为什么printf("hello world")最终在汇编代码中使用了更多的CPU指令(不考虑使用的标准库)而不是cout << "hello world"

对于C ++,我们有:

movl    $.LC0, %esi
movl    $_ZSt4cout, %edi
call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc

对于C:

movl    $.LC0, %eax
movq    %rax, %rdi
movl    $0, %eax
call    printf
  • C ++代码中的第2行和来自?
  • 的C代码中的第2,3行

我正在使用gcc版本4.5.2

4 个答案:

答案 0 :(得分:5)

对于Linux x86_64上的64位gcc -O3(4.5.0),其内容为: cout&lt;&lt; “Hello World”

movl    $11, %edx         ; String length in EDX
movl    $.LC0, %esi       ; String pointer in ESI
movl    $_ZSt4cout, %edi  ; load virtual table entry of "cout" for "ostream"
call    _ZSt16__ostream_insertIcSt11char_traits...basic_ostreamIT_T0_ES6_PKS3_l

printf(“Hello World”)

movl    $.LC0, %edi       ; String pointer to EDI
xorl    %eax, %eax        ; clear EAX (maybe flag for printf=>no stack arguments)
call    printf

这意味着,您的序列完全取决于任何特定的 编译器实现,其版本和可能的编译器选项。 您的编辑状态,您使用gcc 4.5.2(这是相当新的)。 似乎4.5.2引入了额外的64位寄存器 这个顺序无论出于什么原因它将64位RAX保存到RDI 在把它归零之前 - 这绝对没有意义(至少对我而言)。

更有趣:3个参数调用序列( g ++ -O1 -S source.cpp ):

 void c_proc()
{
 printf("%s %s %s", "Hello", "World", "!") ;
}

 void cpp_proc()
{
 std::cout << "Hello " << "World " << "!";
}

导致( c_proc ):

movl    $.LC0, %ecx
movl    $.LC1, %edx
movl    $.LC2, %esi
movl    $.LC3, %edi
movl    $0, %eax
call    printf

.LCx是字符串,没有涉及堆栈指针

对于 cpp_proc

movl    $6, %edx
movl    $.LC4, %esi
movl    $_ZSt4cout, %edi
call    _ZSt16__ostream_insertIcSt11char_traits...basic_ostreamIT_T0_ES6_PKS3_l
movl    $6, %edx
movl    $.LC5, %esi
movl    $_ZSt4cout, %edi
call    _ZSt16__ostream_insertIcSt11char_traits...basic_ostreamIT_T0_ES6_PKS3_l
movl    $1, %edx
movl    $.LC0, %esi
movl    $_ZSt4cout, %edi
call    _ZSt16__ostream_insertIcSt11char_traits...basic_ostreamIT_T0_ES6_PKS3_l

你现在看到的是这一切。

此致

RBO

答案 1 :(得分:1)

调用者代码大部分时间与性能无关。

我猜C ++代码的第2行将std :: cout的地址存储为运算符的隐式“this”参数&lt;&lt;方法

我可能在C部分错了,但在我看来它是不完整的。 rax的32位上半部分未在此片段中初始化,可能会在之前初始化。(不,我在这里错了)。

根据我的理解(我可能是错的),64位寄存器的问题是,大部分时间它们都无法由immediates初始化,所以你必须使用32位操作来获得所需的结果。所以编译器使用32位寄存器来初始化64位rdi寄存器。

似乎printf将al(eax的LSB)的值作为输入告诉printf()有多少xmm 128寄存器用作输入。它看起来像是一个优化,能够将输入字符串传递给xmm寄存器或其他一些有趣的业务。

答案 2 :(得分:1)

int printf( const char*, ...)是一个可变函数,可以接受一个或多个参数;而ostream& operator<< (ostream&, signed char*)恰好是两个。我认为这说明了调用它们所需的指令差异。

C ++反汇编中的第2行是它通过ostream&amp; (在这种情况下cout)。所以函数知道它输出的是什么流对象。

由于两者最终都进行了函数调用,因此这种比较在很大程度上是无关紧要的;在函数调用中执行的代码将更加重要。运算符&lt;&lt;对于许多右侧类型重载,并在编译时解决;另一方面,printf()必须在运行时解析格式字符串以确定数据类型,因此可能会产生额外的开销。无论哪种方式,在函数内执行的代码量将根据执行的指令淹没调用开销,并且几乎肯定会受到在图形显示器上呈现文本所需的OS代码的支配。所以总之,你是冒汗的小东西

答案 3 :(得分:0)

movl移动很长,32位移动

movq是移动四元组,64位移动

printf有一个返回值,无论是写入的字符数还是失败时的-1,并且该值存储在%eax中,这就是所有额外的行都在担心。