2015-03-08 2 views
2

Когда я проверяю коды других людей, я иногда сталкиваюсь с статическими встроенными функциями, реализованными в файлах заголовков, а не в обычных реализациях функций в файлах C.Когда использовать статические встроенные функции вместо обычных функций

Например, cache.h заголовочный файл (https://github.com/git/git/blob/master/cache.h) of git содержит много таких функций. Один из них скопирован ниже;

static inline void copy_cache_entry(struct cache_entry *dst, 
        const struct cache_entry *src) 
{ 
    unsigned int state = dst->ce_flags & CE_HASHED; 

    /* Don't copy hash chain and name */ 
    memcpy(&dst->ce_stat_data, &src->ce_stat_data, 
      offsetof(struct cache_entry, name) - 
      offsetof(struct cache_entry, ce_stat_data)); 

    /* Restore the hash state */ 
    dst->ce_flags = (dst->ce_flags & ~CE_HASHED) | state; 
} 

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

+0

Хорошим ориентиром является использование их только после того, как обширные контрольные показатели продемонстрировали, что это полезно сделать. –

+0

@WilliamPursell В чем заключается недостаток использования их перед тем, как делать какие-либо тесты? Я получу безразличную производительность или худшую производительность по сравнению с обычными функциями. – yasar

ответ

6

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

Таким образом, с точки зрения производительности, это вообще не рекомендуется объявлять функции inline, если они не являются настолько коротки, что их вызов является более дорогим, чем их исполнение.

Чтобы выразить это, вызов функции занимает от 10 до 30 циклов времени процессора (в зависимости от количества аргументов). Арифметические операции обычно выполняются за один цикл, однако загрузка памяти из кеша первого уровня занимает примерно три-четыре цикла. Итак, если ваша функция более сложна, чем простая последовательность, состоящая не более чем из трех обращений к памяти и некоторой арифметики, нет смысла указывать ее.

я обычно беру этот подход:

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

  • Если функция используется только в одном файле, я объявляю ее как static, а не inline. Это приводит к тому, что компилятор может см., когда такая функция используется точно один раз. И если он увидит это, это, скорее всего, приведет к его, независимо от того, насколько он сложный, поскольку он может доказать, что нет недостатка в инкрустации.

  • Все остальные функции не являются ни static, ни inline.


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

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

С этой оптимизацией функция действительно сводится к короткой, простой последовательности. Я не могу сказать, затрагивает ли он много памяти, потому что я не знаю структуру, которая копируется.Если эта структура мала, добавление ключевого слова inline представляется хорошей идеей в этом случае.

1

Основная причина - производительность: при правильном использовании встроенные функции могут позволить компилятору генерировать более эффективный код.

Хорошей стратегией для выявления узких мест в производительности является профилирование кода. Как только это будет сделано, наиболее эффективным способом повышения производительности является сосредоточение внимания на узких местах. Существует много стратегий, таких как алгоритмические улучшения и т. Д. Одной из таких стратегий является краткая часто используемая функция inline.

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

1

От Wikipedia:

В C и C++ языки программирования, инлайн функция является один квалифицируется с ключевым словом inline; это служит двум целям. Во-первых, он служит директивой компилятора, которая предлагает (но не требует), чтобы компилятор заменил тело функции inline, выполнив встроенное расширение, то есть: вставив код функции по адресу каждого вызова функции, тем самым сохраняя накладные расходы на вызов функции. В этом отношении он аналогичен спецификатору класса хранения register, который аналогичным образом дает подсказку оптимизации.

Также ключевое слово static действует как инкапсуляция, поэтому ни один файл снаружи не может использовать эту функцию. Хороший артикул по http://en.wikipedia.org/wiki/Inline_function

1

inline позволяет определять функции в заголовке.

static делает эту функцию доступной только в текущем устройстве перевода.

0

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

Каждая заявленная функция static в строго соответствующей программе может быть объявлена ​​inline. Это лишь подсказка для компилятора и не имеет смыслового значения (nb, для функций с внешней связью, есть разница).

Иногда static inline рассматривается как альтернатива проверки типов для функции макросов и, следовательно, может рассматриваться как обслуживающая некоторые цели документации.

Важно документировать функцию как static и определенную в заголовке (или, по крайней мере, как потенциально static и определенную в заголовке), поскольку пользователь такого заголовка не должен предполагать, что с использованием адреса функции в разных единицах перевода дает равные результаты.

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