Вбудовані функції в C ++. У чому справа?


19

Згідно з тим, що я прочитав, компілятор не зобов'язаний заміщати виклик функції вбудованої функції своїм тілом, але зробить це, якщо зможе. Це змусило мене задуматися - чому ми маємо вбудоване слово, якщо це так? Чому б не зробити всі вбудовані функції за замовчуванням і не дати компілятору розібратися, чи може він замінювати виклики тілом функції чи ні?


1
Here . Сторінка 6
EpsilonVector

Відповіді:


40

inlineє від С; це не було новим для C ++.

Існують ключові слова ( registerі inline) C, які були розроблені, щоб програміст міг допомогти в оптимізації коду. Сьогодні вони, як правило, ігноруються, оскільки компілятори можуть робити краще при призначенні реєстру та вирішенні, коли вбудувати функції (насправді компілятор може вбудовувати або не вбудовувати функцію в різний час). Генерація коду для сучасних процесорів набагато складніша, ніж для детермінованих, звичайних, коли Рітчі вигадував C.

Що слово означає зараз, у C ++, це те, що воно може мати кілька однакових визначень і його потрібно визначати в кожній одиниці перекладу, яка його використовує. (Іншими словами, вам потрібно переконатися, що вона може бути окреслена.) Ви можете мати inlineфункцію в заголовку без проблем, а функції учасників, визначені у визначенні класу, автоматично ефективно inline.


3
+1 для історії inline.
Тамара Війсман

11
Я впевнений, що inlineспочатку був стандартизований на C ++, хоча він уже був доступний як розширення постачальника на C .... так, здається, він був доданий до стандарту C у C99.
Бен Войгт

@Ben Voigt: Ти маєш рацію. Я вперше зіткнувся з цим у C.
Девід Торнлі

5
Також пам’ятайте, що історія не лише помиляється, але правила для inlineC99 та пізніших версій відрізняються від правил для inlineC ++.
Альф П. Штейнбах

27

Спочатку це inlineбув дуже сильний натяк на те, що дзвінки до функції повинні бути окреслені.

Але єдиним гарантованим ефектом inlineє надання можливості визначити функцію (ефективно однаково) у кількох одиницях перекладу, наприклад, розміщення визначення у файлі заголовка.

Сьогодні деякі компілятори дуже прагнуть дотримуватися підказки, наприклад g ++. А деякі компілятори сприймають це менш серйозно, наприклад, Visual C ++. Але всі повинні дотримуватися гарантії.

Прикро, що ці два значення - натяк на оптимізацію та те, що ми можемо назвати визначенням рівня, пов'язане з лінкером, - є одним і тим же ключовим словом, тому що це означає, що одного практично іншого неможливо.

Також прикро, що inline(або, краще, окреме ключове слово, що стосується визначення, що не підлягає застосуванню) ¹можливо застосувати до даних .

Потреба в даних, що знімаються на рівні лінкера, зросла, оскільки модулі, що працюють лише в заголовку, стали більш популярними. Наприклад, багато під-бібліотек Boost мають лише заголовки.

Однак для даних можна застосувати невелику хитрість із шаблонами. Визначте його в якомусь шаблоні класу, надайте typedefпараметр шаблону void(або будь-який інший). Це тому, що правило єдиного визначення робить специфічний виняток для шаблонів.

Примітки:
¹ inlineзмінні будуть підтримуватися в C ++ 17 .


1
+1 для додаткової інформації про inline.
Тамара Війсман

1
" ефективно ідентично " - це насправді дуже хитра умова, оскільки дуже погано розроблені мови C ++ намагаються зробити так, щоб нормально грамотні та обережні програмісти писали розумний, але формально невизначений код, який використовує статичні об'єкти - мені цікаво, скільки членів комітету потрапляють у пастку самі
curiousguy

6

Чому б не зробити всі функції вбудованими за замовчуванням? Тому що це інженерна торгівля. Існує щонайменше два типи "оптимізації": прискорення програми та зменшення розміру (слід пам'яті) програми. Вбудовування, як правило, пришвидшує справи. Це позбавляється від набору функціональних викликів, уникаючи натискання та витягування параметрів із стека. Однак це також збільшує слід пам'яті програми, оскільки кожен виклик функції тепер повинен бути замінений на повний код функції. Щоб зробити це ще складніше, пам’ятайте, що процесор зберігає часто використовувані шматки пам'яті в кеші на процесорі для надшвидкого доступу. Якщо зробити зображення пам'яті програми достатньо великим, програма не зможе ефективно використовувати кеш, і в гіршому випадку вбудова може фактично уповільнити програму.


5
Але компілятор може (і це робить!) Зрозуміти це набагато краще, ніж програміст загалом. Тож це не вагомий аргумент.
Конрад Рудольф

" кожен виклик функції тепер повинен бути замінений на повний код функції ". І вбудована функція не є функцією, де послідовність виклику опущена і де код монтажу функції скопійований на місце. Функція вбудовування складається на місці, шляхом приведення проміжного проміжного коду високого рівня. Це дозволяє компілятору ефективно розглядати тіло функції як чистий макрос (чистий макрос - той, який не має примх макросів препроцесора С), з потенційно можливою безліччю оптимізацій.
цікавогут

3

Щоб зрозуміти "інлійн", вам потрібно зрозуміти історію і те, яким було життя 20 (і 30) років тому.

Ми писали код на комп’ютерах, у яких мало пам'яті, тому компілятору не вдалося обробити весь код, який складав програму за один раз. Компілятор також був дуже повільним, тому вам не потрібно було перекомпілювати код, який не змінився - за 24 години (на комп’ютері, який коштував більше, ніж автомобіль верхнього кінця) для перекомпіляції всього коду було нормально для кількох проектів. працював над.

Тому кожен файл коду був окремо зібраний у файли об'єктів. Кожен об’єктний файл починався зі списку всієї функції, що містилася, разом із "адресою" функції. Об'єктний файл також мав перелік усіх функцій, які він викликав в інших файлах об'єкта, а також розташування виклику.

Спочатку лінкер прочитав би всі об'єктні файли та створив список усіх експортованих функцій, а також файл, у якому вони знаходились, та адресу. Потім він буде перечитати всі об’єктні файли, вивівши їх у програмний файл, при цьому оновивши всі "зовнішні" виклики функції з адресою функції.

Лінкер не змінив і не оптимізував машинний код, створений компілятором, інакше, ніж виправити посилання на зовнішні виклики функцій. Лінкер був частиною операційної системи і передував більшості компіляторів. Коли люди написали новий компілятор, їм знадобився, щоб він працював з поточними посиланнями, і щоб мати можливість посилатися на поточні файли об'єктів, інакше системні дзвінки не могли здійснюватися.

Компілятор лише коли-небудь бачив код у файлі ".c" або ".cpp", який він компілював разом із усіма включеними файлами заголовків. Тож не вдалося зробити оптимізацію на основі коду в інших файлах ".c" або ".cpp".

Ключове слово "вбудований" дозволило визначити тіло функції (методу) у файлі заголовка, отже, дозволило компілятору використовувати код функції під час компіляції коду, який її викликає. Наприклад, скажімо, що у вас був клас колекції, визначений у файлі anther .cpp, у цього класу був би метод "isEmpty", який містив би один рядок коду, було б велике прискорення результуючої програми, якщо замість виклику функції , виклик функції було замінено на цю лінію.

Ключове слово "вбудований" в той час розглядалося як "дешевий і простий" спосіб дозволити інкапсуляцію даних, уникаючи витрат на виклики функцій, без цього багато програмістів мали б просто отримати доступ до приватних полів об'єкта. (Макроси, де набагато гірший спосіб "вбудовування" коду, який там, де поширений на той час.)

У наші дні "лінкери" роблять багато оптимізації коду і, як правило, деякі команди записують як компілятор. Компілятор часто просто перевіряє правильність коду і «стискає» його, залишаючи більшість завдань створення машинного коду посилання.


2

Подивимося, що говорить стандарт (виділені важливі частини жирним шрифтом):

2. Декларація функції із вбудованим специфікатором оголошує вбудовану функцію. Вбудований специфікатор вказує на реалізацію, що заміна вбудованого функції функції в точці виклику повинна віддавати перевагу звичайному механізму виклику функції. Для здійснення цієї заміни в рядку в точці виклику не потрібна реалізація ; однак, навіть якщо ця заміна вбудованої форми опущена, інші правила функцій вбудованого режиму все одно повинні дотримуватися

- стандарт C ++, ISO / IEC 14882: 2003 , 7.1.2 Функціональні характеристики [dcl.fct.spec]

Отже, якщо ви хочете бути впевненими, вам слід ознайомитися з документацією вашого компілятора.

Складати все - це погана ідея, оскільки це може призвести до безлічі копій машинного коду ...

Тож вам доведеться знати:

Немає простих відповідей: Ви повинні пограти з нею, щоб побачити, що найкраще. Ви НЕ погоджуйтеся на спрощених відповідей , як «Використання Never inlineфункції» або «Завжди використовувати inlineфункції» або «Використовувати inlineфункції тоді і тільки тоді , коли функція є менше , ніж N рядків коду.» Ці правила одного розміру можуть бути легко записати, але вони дадуть неоптимальні результати.

- C ++ поширені запитання, вбудовані функції , 9.3 Чи inlineпокращують функції продуктивність?


1
Те, що ви говорите, є правдивим, але не актуальним, тому що як програміст, це не ваше рішення, вводити чи ні. Якщо ви введете ключове слово, ви можете або не можете впорядкувати свої функції. Ви не вводите ключові слова, ви все ще можете або не можете їх накреслити. Немає жодних правил щодо того, коли ви вводите ключове слово, що з компілятором є тим, хто насправді приймає рішення.
Кейт Григорій

Як це не актуально, якщо він говорить точно так само, як і ви? Немає простих відповідей .
Тамара Війсман

Це не актуально, оскільки не відповідає на питання ОП. Він не запитує "що робить Inline", він запитує "в чому сенс". І ваша відповідь просто повторює те, що ми всі вже знаємо про inline.
Давор Ждрало

@Davor: "У чому сенс?" не дотримується поширених запитань, тому я відповідаю на питання, поставлені в питанні. Я кажу про те inline, що повторюю свої знання, оскільки ОП, здається, пропускає частину, і це є основною причиною, чому він не отримує суть. Щоб отримати бал, йому потрібно зрозуміти, що знаходиться під цією точкою ...
Тамара Війсман

Чому б чорт не дотримувався FAQ? Стандарт описує синтаксис та семантику вбудованого ключового слова, а ОП питання - яка мета, коли його слід використовувати, які проблеми він повинен вирішити? Я не бачу, як це не дотримується FAQ.
Давор Ждрало

1

Дозвольте навести вагому причину для використання вбудованого ключового слова.

У вбудованій системі, наприклад, на принтері квитків або подібній меншій системі. Процесор дуже обмежений, і функція виклику (підготовка параметрів функції на стеці, виклик, виведення параметрів з стека та повернення відповіді тощо) може зайняти кілька мс для виконання поруч із самою функцією.

Скажімо, час виклику становить близько 60 мс (лише для виклику, а не фактичної функції), і у вас є 50 ітерацій (цикл або ітераційні дзвінки в дереві).

Час просто рухатися вперед і назад від цього виклику функції займе 60 * 50 = 3000 (3 секунди).

Якщо у вас є пам'ять, ви обов'язково зробите вбудований, щоб заощадити 3 секунди.

Таким чином, вбудовані в основному використовуються, коли вам потрібна швидкість виконання. У деяких проектах, з якими я брав участь, час виклику був довшим, ніж час виконання, класична ситуація, коли потрібно використовувати вбудований.


Е, що? Введення вбудованого коду не означає, що компілятор насправді вбудує його в дію, і не помістити його також не означає, що він не буде. Це лише натяк, який переважно ігнорують компілятори сьогодні. Якщо компілятор вважає корисним вбудований текст, він, інакше, не буде. У вас там немає реального контролю. ОП вже це знає і, питаючи, цитую: "Який сенс?". Хто, на чорт, дає +1 за це? Це і вводить в оману (маючи на увазі, що вбудоване ключове слово примушує вбудовування), і не має значення для питання.
Давор Ждрало

2
@Davor Ždralo Не потрібно бути грубим. Я інтерпретував питання як ЧОМУ я повинен використовувати рядки? Моя суть полягала в тому, щоб показати, що ви можете отримати код швидше за рахунок пам'яті. Кожен компілятор може по-різному обробляти ключове слово inline, тому вам потрібно перевірити документацію, яку я не згадував. Оскільки ви не завжди можете дозволити собі додатковий накладний обсяг пам'яті, корисно "керувати", коли ним користуватися. Я також не сказав, що рядки виконуються. Хтось вважав цю відповідь корисною для нього та проголосував +1, будь ласка, поважайте, що інші можуть мати інший рівень досвіду та знайдіть щось корисне.
Макс Кілланд
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.