Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

fastcall

399
09 июля 2010 года
KIV
432 / / 20.01.2009
И так у меня вопрос. Зачем нужен fastcall (сразу хочу сказать, что мой вопрос особенно касается 64-битных систем, где он по умолчанию)? Если писать на асме, то ещё понятно. Но ведь язык высокого уровня (С++, например. я скомпилил пустую подпрограмму, а потом посмотрел ассемблерный листинг и мои предположения подтвердились) всегда их перемещает в стек (правда не push, а mov, потому что место в стеке зарезервировано уже вызывающей процедурой). Тогда какой выигрыш в скорости? Не проще ли просто push запихивать все параметры в стек (конечно, вы можете сказать, что значения больше 2^31 в длинном режиме запихнуть в стек не выйдет, но это случается реже и тогда можно сначала поместить это значение в регистр, а потом регистр запихнуть в стек)? Единственное предположение, что push медленнее двух mov - один mov в регистр, а второй из регистра в память. Хотя это не самое правдоподобное предположение...
14
09 июля 2010 года
Phodopus
3.3K / / 19.06.2008
Цитата: KIV
И так у меня вопрос. Зачем нужен fastcall (сразу хочу сказать, что мой вопрос особенно касается 64-битных систем, где он по умолчанию)?


Очевидно он немного быстрее, и в 64-битных системах дофига регистров.

Цитата: KIV

Но ведь язык высокого уровня (С++, например. я скомпилил пустую подпрограмму, а потом посмотрел ассемблерный листинг и мои предположения подтвердились) всегда их перемещает в стек


Кого он помещает? Давайте разбирать на конкретном примере. fastcall вообще придумка разработчиков некоторых компиляторов, о стандартизированности я не слышал.

399
09 июля 2010 года
KIV
432 / / 20.01.2009
Цитата:
Очевидно он немного быстрее, и в 64-битных системах дофига регистров.


Про регистры я знаю. Только вот используются в первую очередь rcx, rdx, rsi и rdi, которые затруться при большинстве инструций (умножение, деление, циклы, строковые команды). Не всегда в принцыпе возможно построить процедуру так, чтобы значения сохранились до их последнего использования. Ну а даже в тех случаях, когда это возможно, компилятор всё равно не сможет догадаться как именно это сделать. Поэтому как ни странно компилятор сразуже копирует значения из регистров в стек. Тогда вопрос - зачем их вообще сначала помещать в регистры? Почему бы сразу не запихнуть в стек с помощью push? Или два mov (причём один обязательно направлени в память) быстрее одного push? Маловероятно. По объёму уж точно занимает больше. Тогда какой прирост производительности? Ну или уменьшение размера?
Для проверки я написал программу на C++:

 
Код:
void a(int p1, int p2, int p3, int p4, int p5, int p6) {
}

Затем скомпилировал 64-битным gcc под Linux. Причём скомпилировал с ключом -S. Это даёт только ассемблерный код. Без создания объектного файла и линкования. Вот асм. код касающийся процедуры a:
Код:
pushq   %rbp
.LCFI0:
    .cfi_def_cfa_offset 16
    movq    %rsp, %rbp
    .cfi_offset 6, -16
.LCFI1:
    .cfi_def_cfa_register 6
    movl    %edi, -4(%rbp)
    movl    %esi, -8(%rbp)
    movl    %edx, -12(%rbp)
    movl    %ecx, -16(%rbp)
    movl    %r8d, -20(%rbp)
    movl    %r9d, -24(%rbp)
    .loc 1 8 0
    leave
    ret

Как можно заметить параметры из регистров были скопированы в стек. Зачем же тогда придумали fastcall? Я предполагаю, что просто, что-то не знаю, а на самом деле fastcall даёт прирост производительности...
14
12 июля 2010 года
Phodopus
3.3K / / 19.06.2008
А вы попробуйте другие компиляторы, посмотрим.
399
12 июля 2010 года
KIV
432 / / 20.01.2009
Ну вы хотя бы посоветуйте, что использовать. Причём компилятор должен быть 64-битный и желательно под Linux. И всё же я думаю, что не в компиляторе дело. Действительно, регистры RCX и RDX затрутся почти сразу. Ведь команда, скажем, умножения если у неё операнды не размером с байт, уже изменяет RAX и RDX (или EAX и EDX, или AX и DX). Практически в любой подпрограмме прямо или косвенно используется умножение. Компилятор не может поменять местами инструкции. Да это и помогло бы в редких случаях. Например, предположим параметр a предаётся в RDX. Что делать с таким кодом:
 
Код:
b = a % 10;
c = a / 10;

Как сделать, чтобы в c был тоже верный результат? Только путём сохранения переменной в другое место. А ещё из программы может потребоваться вызвать другую... Таким образом я прихожу к выводу, что переменные в 99% случаев всё же придётся сохранить в стеке. Даже если писать программу на ассемблере мало подпрограмм получиться оптимизировать для fastcall (если в ней содержится хотя бы один вызов или команда умножения или деления, то ничего не выйдет). Ну а компилятор - не человек. Он сможет сооптимизировать для fastcall ещё меньше подпрограмм (если учесть, что порядок команд изменять нельзя, то это число приближается к 0).
14
12 июля 2010 года
Phodopus
3.3K / / 19.06.2008
Что использовать? Intel, MS, Sun... Что достанете :)
А если представить себе значительный стек вызовов (что не редкость в современных библиотеках) где над параметрами действий практически не производится (ну разве приведение типа - действие?) а подпрограммы все вызываются и вызываются?
399
13 июля 2010 года
KIV
432 / / 20.01.2009
Хм... Ну если только в таком случае (мысленно добавьте приведения типов и т. п.):
 
Код:
void a(int p1, int p2) {
  b(p1, p2);
}
void b(int p1, int p2) {
  c(p1, p2);
}
...

Да. В таких случаях fastcall действительно немного увеличит производительность...
14
14 июля 2010 года
Phodopus
3.3K / / 19.06.2008
Короче походу просто из двух зол выбрали меньшее.
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог