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

StackOverflow https://stackoverflow.com/questions/2750130

  •  02-10-2019
  •  | 
  •  

Вопрос

Я создаю векторно-матричную библиотеку.(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-й подписи, чтобы отразить предложения.
Это было полезно?

Решение

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top