2009-08-26 3 views
4

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

+0

. Создание общего файла объекта увеличит время выполнения. У меня действительно нет представления об этом. – 2009-08-26 18:39:31

ответ

6

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

+0

профиль показывает, что время равномерно распространяется на отдельные функции .. означает ли это, что нет улучшения? – 2009-08-26 18:38:02

+0

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

0

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

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

Это не простой вопрос для ответа, даже если вы предоставили нам несколько подробностей.

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

3

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

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

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

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

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

1

Накладные расходы вызова функций, в основном сочетание:

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

Поэтому, чтобы начать задавать вопросы:

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

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

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

+0

спасибо .. я проверю накладные расходы .. – 2009-08-26 18:42:34

0

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

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

0

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

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

Смежные вопросы