Какой самый быстрый способ вызова и выполнения функции в C? - PullRequest
4 голосов
/ 26 августа 2009

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

Ответы [ 7 ]

6 голосов
/ 26 августа 2009

Вам нужно профилировать вашу программу, чтобы знать, если это проблема. Если вы тратите 99% своего времени на выполнение отдельных функций, самое лучшее улучшение, на которое вы можете надеяться, - 1%, и даже это вряд ли.

3 голосов
/ 26 августа 2009

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

То есть что-то вроде:

void foo(void)
{
    /* do some stuff */
}

int main(void)
{
    foo();
}

Может быть встроено в:

int main(void)
{
    /* do some stuff */
}

Но если компилятор не знает, какой из них вызвать:

void foo(void)
{
    /* do some stuff */
}

void bar(void)
{
    /* do some other stuff */
}

typedef void(*Function)(void);

int main(void)
{
    Function func = /* choose a function at runtime */
    func();
}

Компилятор не может предсказать, какая функция будет вызвана, и, следовательно, не может встроить ее.

Если ваш компилятор поддерживает это, вы можете попробовать использовать __fastcall, но вам нужно профилировать свой код и посмотреть, если он имеет положительное значение.

Этот уровень косвенности не будет иметь большого значения. Профилируйте свой код и найдите реальное замедление.

1 голос
/ 26 августа 2009

Издержки при вызове функций в основном представляют собой комбинацию:

  • сам вызов функции
  • параметры, которые вы передаете
  • значение повторной настройки
  • сколько раз вам нужно вызвать функцию

Итак, для начала задайте вопросы:

  • Можете ли вы изменить алгоритм, чтобы потребовать меньше вызовов функций?
  • можете ли вы уменьшить объем данных, которые вы должны передавать туда и обратно?
  • Вы можете изменить алгоритм для пакетной обработки его вызовов для каждой функции (чтобы можно было обрабатывать группу значений за один вызов или, по крайней мере, повторно вызывать одну и ту же функцию для группы значений, чтобы весь код оставался в Кэш-память процессора)?

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

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

1 голос
/ 26 августа 2009

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

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

0 голосов
/ 26 августа 2009

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

По большому счету, сбривание нескольких инструкций здесь или там не приведет к каким-либо существенным улучшениям. Большие победы придут от улучшения ваших алгоритмов.

0 голосов
/ 26 августа 2009

Вы должны использовать такой инструмент, как QProf , Valgrind или gprof, чтобы профилировать ваш код и посмотреть, где больше всего времени затрачивается на выполнение. Основываясь на результатах, вы должны оптимизировать функции, которые занимают больше всего времени.

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

0 голосов
/ 26 августа 2009

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

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

На этот вопрос не так просто ответить, даже если вы ДАЛИ нам еще несколько деталей.

Как говорит Марк ... Профилировщик - твой друг.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...