Встроенная функция и стоимость вызова в C

Я делаю библиотеку векторов / матриц. (GCC, ARM NEON, iPhone)

typedef struct{ float v[4]; } Vector;
typedef struct{ Vector v[4]; } Matrix;

Я передал struct data как указатель, чтобы избежать снижения производительности при копировании данных при вызове функции. Итак, сначала я разработал такую ​​функцию:

void makeTranslation(const Vector* factor, Matrix* restrict result);

Но если функция встроенная, есть ли причина передавать значения в качестве указателя производительности? Эти переменные тоже копируются? Как насчет регистрации и кешей? Я попытался изменить функцию так:

inline Matrix makeTranslation(const Vector factor) __attribute__ ((always_inline));

Как вы относитесь к стоимости звонков в каждом случае?

  • Я добавил const во 2-ю подпись, чтобы отразить предложения.

1 ответ

Решение

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

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

Из-за передачи по указателю этот код ВСЕГДА будет иметь инструкцию для получения адреса объекта и, по крайней мере, одну разыменование для получения значения члена. Если вы передадите по значению, это МОЖЕТ произойти, но компилятор МОЖЕТ оптимизировать все это.

Не забывайте, что чрезмерное использование встраивания может значительно увеличить размер двоичного кода компилятора. В некоторых случаях наличие больших сегментов кода (как результат встроенных функций) может привести к большему количеству промахов в кэше команд, что приведет к снижению производительности, поскольку ЦП постоянно приходится выходить в основную память для выборки частей вашей программы, поскольку некоторые из них слишком большой, чтобы поместиться в маленький кэш L1. Это может быть особенно важно во встроенных процессорах (таких как iPhone), потому что эти процессоры обычно имеют небольшой кэш.

Другие вопросы по тегам