繁体   English   中英

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

[英]GCC/g++ cout << vs. printf()

  • 为什么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版

对于Linux x86_64上的64位gcc -O3(4.5.0),它读取: cout <<“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

调用者代码大多数时间与性能无关。

我猜C ++代码的第2行将std :: cout的地址存储为operator <<方法的隐式'this'参数。

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

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

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

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

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

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

movl移动很长,32位移动

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM