GCC / g ++ cout << против printf () - PullRequest
7 голосов
/ 06 марта 2011
  • Почему printf("hello world") заканчивается использованием большего количества инструкций ЦП в собранном коде (без учета используемой стандартной библиотеки), чем 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
  • ЧТО ТАКОЕ строка 2 из кода C ++ и строки 2,3 из кода C для?

Я использую gcc версии 4.5.2

Ответы [ 4 ]

5 голосов
/ 06 марта 2011

Для 64-битного gcc -O3 (4.5.0) в Linux x86_64 это читается как: cout << "Hello World" </strong>

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

Теперь вы понимаете, что это такое.

Привет

БВУ

1 голос
/ 06 марта 2011

int printf( const char*, ...) - это переменная функция, которая может принимать один или несколько аргументов;тогда как ostream& operator<< (ostream&, signed char*) занимает ровно два.Я считаю, что это объясняет разницу в инструкциях, необходимых для их вызова.

Строка 2 в разборке C ++ - это место, где она передает ostream & (в данном случае cout).поэтому функция знает, в какой объект потока она выводится.

Поскольку оба в конечном итоге выполняют вызов функции, сравнение в значительной степени не имеет значения;код, выполняемый в вызове функции, будет гораздо более значимым.Оператор << перегружен для ряда правосторонних типов и разрешается во время компиляции;printf (), с другой стороны, должен проанализировать строку формата во время выполнения, чтобы определить тип данных, что может привести к дополнительным расходам.В любом случае объем кода, выполняемого в функциях, будет затягивать накладные расходы с точки зрения выполнения инструкций, и почти наверняка будет зависеть код ОС, необходимый для визуализации текста на графическом дисплее.Короче говоря, вы <em>потеете мелочи .

1 голос
/ 06 марта 2011

Код вызывающей стороны большую часть времени не имеет отношения к производительности.

Я предполагаю, что строка 2 кода C ++ хранит адрес std :: cout в качестве неявного аргумента 'this' оператора <<method. </p>

и я могу ошибаться в части C, но мне кажется, что она неполная.32-битная верхняя часть rax не инициализирована в этом фрагменте, она может быть инициализирована раньше. (нет, я ошибаюсь).

из того, что я понимаю (я могу ошибаться),Проблема с 64-битными регистрами состоит в том, что большую часть времени они не могут быть инициализированы непосредственными инициаторами, поэтому вам нужно поиграть с 32-битными операциями, чтобы получить желаемый результат.таким образом, компилятор играет с 32-битными регистрами, чтобы инициализировать 64-битный регистр rdi.

И кажется, что printf принимает значение al (младший бит eax) в качестве ввода, которое сообщает printf (), сколько xmm 128 регистровиспользуется в качестве ввода.Похоже на оптимизацию, чтобы можно было передавать входную строку в регистры xmm или какую-то другую забавную работу.

0 голосов
/ 06 марта 2011

movl - это длинный ход, 32-разрядный ход

movq - это перемещение quad, 64-битный ход

printf имеет возвращаемое значение, либо количество написанных символов, либо -1 при ошибкеи это значение сохраняется в% eax, это все, что беспокоит лишнюю строку.

...