2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




Начать новую тему Ответить на тему На страницу 1, 2  След.
 
 C++, inline функции
Сообщение29.08.2016, 10:52 
Аватара пользователя


28/05/15
74
Допустим, у нас есть короткая функция:

Код:
inline int squared(const int argument)
{
    return argument * argument;
}


Следуя рекомендациям, ставим ей спецификатор inline (некоторые пишут, что она итак заинлайнится компилятором, так как очень маленькая, но это сути не меняет). Тогда, как везде написано про inline, компилятор подставит код функции в место её вызова, а не сделает функцию. То есть, казалось бы, код:

Код:
// ...
int x = squared(y);
// ...


будет полностью эквивалентен коду

Код:
// ...
int x = y * y;
// ...


Рассмотрим теперь ситуацию, когда код вызова чуток другой, а именно - аргументом будет не переменная, а возвращённое из другой функции значение:

Код:
// ...
int x = squared(my_function(y));
// ...


Казалось бы, этот код, по ранее описанной логике, должен развернуться в

Код:
// ...
int x = my_function(y) * my_function(y);
// ...


, что приведёт к двойному вызову my_function(y), если последняя функция вычисляется долго, то мы сразу схватим падение производительности. Если бы функция не заинлайнилась, то сначала бы вызвалась my_function(y), и её вычисленное значение было бы подставлено в качестве аргумента функции squared(...), то есть код был бы эквивалентен чему-то типа:

Код:
// ...
int arg = my_function(y);
int x = arg * arg;
// ...


Если inline работает именно так, то это очень плохо, так как получается, что результат компиляции зависит от того, будет ли функция заинлайнена, или нет, а не от того, что написано в коде. Плюс в описанном примере (а подобных можно сделать немало) такой инлайнинг напрочь уничтожает преимущества от использования функции (и вообще, в этом примере тогда инлайнинг нужно бы как-то запретить).

Вопрос - правильно ли я понял, что inline функции работает так, как описано выше? У них действительно возникает такой косяк (то есть, по сути описанная функция squared(...) ничем не отличается от макроса
Код:
#define squared(A) (A) * (A)
)? Или же я неправ, и при инлайнинге каким-то образом всё же будет вычислено значение аргумента, и функция будет "будто бы вызвана" от вычисленного значения, а не просто будет подставлено выражение?

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

Кстати, возможно этот вопрос можно уяснить через тестовый проект, который потом дизассемблером прогнать, но я не умею этого делать, да и вообще с ассемблером пока совсем плох, поэтому спрашиваю - может есть люди, которые точно знают ответ на этот вопрос :-) .

 Профиль  
                  
 
 Re: C++, inline функции
Сообщение29.08.2016, 11:07 
Заслуженный участник


26/05/14
981
Этого косяка нет. Компилятор (стандарт языка C++) гарантирует, что в независимости от наличия или отсутствия inline поведение программы не меняется. my_function в вашем примере вызовется один раз.
Это написано в предположении что тело my_function неизвестно компилятору. Если тело известно и в нём нет побочных эффектов, то компилятор может вычислить её сколько угодно раз или подставить как угодно. Если тело известно и в нём есть побочные эффекты, то эти эффекты буду произведены один раз в результирующем коде.
Можно как угодно долго жонглировать возможностями. Но все они следуют одному правилу: добавление inline не меняет наблюдаемое поведение программы.

От себя добавлю, что пирамида потребностей программиста выглядит так: функциональность, нефункциональные требования. В применении к inline это означает, что в вашей программе его быть не должно, пока вы не запустили профилировщик и не обнаружили, что время тратится на вызов конкретной функции. Вы запускали профилировщик?

 Профиль  
                  
 
 Re: C++, inline функции
Сообщение29.08.2016, 12:03 
Заслуженный участник
Аватара пользователя


01/09/13
4656
zcorvid в сообщении #1147288 писал(а):
Или же я неправ, и при инлайнинге каким-то образом всё же будет вычислено значение аргумента

Так в предыдущем примере кода Вы и написали как оно будет по сути.

zcorvid в сообщении #1147288 писал(а):
возможно этот вопрос можно уяснить через тестовый проект
В my_function можно сделать инкремент глобальной переменной...
zcorvid в сообщении #1147288 писал(а):
потом дизассемблером прогнать
Проще сказать компилятору сгенерировать ассемблерный листинг.
Но надо иметь ввиду, что если поведение не регламентируется стандартом, то разные компиляторы могут давать разные результаты.
Но как уже сказали,
slavav в сообщении #1147290 писал(а):
они следуют одному правилу: добавление inline не меняет наблюдаемое поведение программы.

 Профиль  
                  
 
 Re: C++, inline функции
Сообщение29.08.2016, 12:11 
Заслуженный участник


04/03/09
910
Вот конкретный пример:
Используется синтаксис C++
int my_function(int);

__declspec(noinline) int squared(const int argument)
{
        return argument * argument;
}

int main()
{
        int res = squared(my_function(1));
        return res;
}

Соответствующий код на языке ассемблера:
код: [ скачать ] [ спрятать ]
Используется синтаксис ASM
; int __cdecl main()
_main proc near
call    ?my_function@@YAHH@Z ; my_function(int)
mov     ecx, eax
jmp     ?squared@@YAHH@Z ; squared(int)
_main endp

; int __usercall squared@<eax>(const int argument@<ecx>)
?squared@@YAHH@Z proc near
argument = ecx
imul    argument, argument
mov     eax, argument
retn
?squared@@YAHH@Z endp
 


И в случае инлайна:
Используется синтаксис C++
int my_function(int);

inline int squared(const int argument)
{
        return argument * argument;
}

int main()
{
        int res = squared(my_function(1));
        return res;
}

Соответствующий код на языке ассемблера:
Используется синтаксис ASM
; int __cdecl main()
_main proc near
call    ?my_function@@YAHH@Z ; my_function(int)
imul    eax, eax
retn
_main endp
 

Как видно, поведение идентично. Зато при инлайн функции сэкономлено 4 инструкции.

 Профиль  
                  
 
 Re: C++, inline функции
Сообщение30.08.2016, 09:37 
Аватара пользователя


28/05/15
74
slavav в сообщении #1147290 писал(а):
Этого косяка нет. Компилятор (стандарт языка C++) гарантирует, что в независимости от наличия или отсутствия inline поведение программы не меняется. my_function в вашем примере вызовется один раз.
Это написано в предположении что тело my_function неизвестно компилятору. Если тело известно и в нём нет побочных эффектов, то компилятор может вычислить её сколько угодно раз или подставить как угодно. Если тело известно и в нём есть побочные эффекты, то эти эффекты буду произведены один раз в результирующем коде.
Можно как угодно долго жонглировать возможностями. Но все они следуют одному правилу: добавление inline не меняет наблюдаемое поведение программы.

От себя добавлю, что пирамида потребностей программиста выглядит так: функциональность, нефункциональные требования. В применении к inline это означает, что в вашей программе его быть не должно, пока вы не запустили профилировщик и не обнаружили, что время тратится на вызов конкретной функции. Вы запускали профилировщик?


В принципе не очень понятно, как компилятор поймёт, что есть "побочный эффект" увеличения времени вычисления (например в fy_function есть перебор большого массива), но суть я уловил, спасибо.

Профилировщик я не запускал, но сажать везде inline'ы - не моя идея, я спорить не стал, а сейчас вот задумался, не вредит ли инлайн на самом деле... Сам бы я его вообще не использовал.

12d3 в сообщении #1147303 писал(а):
Вот конкретный пример: ...


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

PS. А каким компилятором вы сделали ассемблерный листинг? Или это дизассемблер? Я в Qt4 не нашёл как листинг сделать, но пока не очень активно искал, может способ там есть...

 Профиль  
                  
 
 Re: C++, inline функции
Сообщение30.08.2016, 10:22 
Заслуженный участник


26/05/14
981
zcorvid в сообщении #1147726 писал(а):
В принципе не очень понятно, как компилятор поймёт, что есть "побочный эффект" увеличения времени вычисления (например в fy_function есть перебор большого массива), но суть я уловил, спасибо.

Профилировщик я не запускал, но сажать везде inline'ы - не моя идея, я спорить не стал, а сейчас вот задумался, не вредит ли инлайн на самом деле... Сам бы я его вообще не использовал.

Если компилятор не может понять есть побочный эффект или нет, он выбирает худший (для оптимизации) вариант - побочный эффект есть. Здесь как в медицине - "не навреди".

inline - это рекомендация. Компилятор может принять к сведению, а может проигнорировать. Ему разрешено подставлять функции без inline, разрешено вызывать функции с inline.

inline не всегда ускоряет программу. Вот пример: большая программа, много функций inline, цикл:
Код:
while (true) {
    // a lot of inlined code
    // ...
    if (rare_condition())
        inline_call();
}

Так как rare_condition редко срабатывает, то наличие inline у inline_call() почти не влияет на производительность.
С другой стороны вызов inline_call разворачивается в большое количество кода. После его развёртывания код цикла перестаёт лезть целиком в кеш инструкций процессора и вы получаете многократное замедление вычислений (циклы должны лезь в кеш, если вам нужна скорость).
Написали inline - получили замедление.
Всё ещё хуже - inline вы пишите на одной функции. Вызывается она в десятке мест. Одни места ускорились, другие замедлились.
Всё ещё хуже - не вы один пишете inline. STL набита им под завязку. Любой цикл с STL распухает многократно. Все header-only libs туда же.
А ещё весь развёрнутый код делит один набор регистров. Оптимизатор может отдать большую часть регистров на вызов inline_code, отобрав их у другого кода которому они нужны как воздух.
В итоге ситуация такая - начинающий программист обращает внимание на производительность и набивает руку на небольших проектах. В таких условиях inline выступает как полезное средство получения производительности из ничего. Затем тот же программист попадает на большой проект, где inline в лучшем случае не приносит пользы, в худшем приносит вред.
Конечно авторы компиляторов в курсе проблемы. Поэтому они в большинстве сложных ситуаций просто игнорируют ваши усилия по расстановке inline.

 Профиль  
                  
 
 Re: C++, inline функции
Сообщение30.08.2016, 11:06 


10/04/12
705
Тут другая логика, int x = squared(y); раскрывается как

Используется синтаксис C++
const int argument = y;
int result = argument * argument;
int x = result;
 

 Профиль  
                  
 
 Re: C++, inline функции
Сообщение30.08.2016, 14:21 


11/12/14
893
zcorvid в сообщении #1147726 писал(а):
В принципе не очень понятно, как компилятор поймёт, что есть "побочный эффект" увеличения времени вычисления


Увеличение времени вычисления не является побочным эффектом.

 Профиль  
                  
 
 Re: C++, inline функции
Сообщение06.09.2016, 02:49 
Аватара пользователя


07/02/12
1439
Питер
Ставьте inline там, где считаете, что это необходимо, исходя из статистических соображений.
Часто компилятор не может эффективно оценить частоту вызова функций (если только циклы не с константами).

Надо заметить, что inline не всегда хорошо даже в контексте производительности:
Вставлять inline - значит убрать вызов подпрограммы, а также дать компилятору дополнительную свободу в распихивании переменных по доступным регистрам.
Но с другой стороны - вставлять inline - значит увеличивать объем кода, т.е. перегружать конвееры современных сложных процессоров. А в экзотических случаях - и code-кеш-память.

 Профиль  
                  
 
 Re: C++, inline функции
Сообщение09.09.2016, 18:56 


28/04/16

57
Написать две копии программы, одну с инлайнами, другую без - и сравнить быстродействие.

 Профиль  
                  
 
 Re: C++, inline функции
Сообщение09.09.2016, 22:54 
Аватара пользователя


07/02/12
1439
Питер
petrov_ich
При таком подходе лучше написать 2^N программ, где N - количество кандидатов в inline. И выбрать лучшую

 Профиль  
                  
 
 Re: C++, inline функции
Сообщение09.09.2016, 23:07 
Заслуженный участник


26/05/14
981
bondkim137 в сообщении #1150399 писал(а):
При таком подходе лучше написать 2^N программ, где N - количество кандидатов в inline. И выбрать лучшую

Всё уже придумали до нас: https://en.wikipedia.org/wiki/Superoptimization
Эта штука вообще пытается построить оптимальный код независимо от расстановки inline и прочих усилий программистов.

Ещё раз напомню, что оптимизировать можно только имея на руках профайл который указывает на проблемные места.
Дональд Кнут: "premature optimization is the root of all evil" (http://c2.com/cgi/wiki?PrematureOptimization, https://ru.wikiquote.org/wiki/%D0%94%D0 ... 1%83%D1%82)
Расстановка inline при написании кода противоречит такому подходу.

 Профиль  
                  
 
 Re: C++, inline функции
Сообщение09.09.2016, 23:18 
Аватара пользователя


07/02/12
1439
Питер
slavav
bondkim137 в сообщении #1149514 писал(а):
Часто компилятор не может эффективно оценить частоту вызова функций (если только циклы не с константами).

Относится и к Superoptimization

 Профиль  
                  
 
 Re: C++, inline функции
Сообщение11.09.2016, 20:33 


19/03/09
130
Чаще нужен no_inline __declspec(noinline) в классах

 Профиль  
                  
 
 Re: C++, inline функции
Сообщение11.09.2016, 23:30 
Аватара пользователя


07/02/12
1439
Питер
green5,
например?

 Профиль  
                  
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 27 ]  На страницу 1, 2  След.

Модераторы: Karan, Toucan, PAV, maxal, Супермодераторы



Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group