По сравнению с
- Простой доступ к памяти
- Доступ к диску
- Доступ к памяти на другом компьютере (в той же сети)
- Доступ к диску на другом компьютере (в той же сети)
в С++ на окнах.
По сравнению с
в С++ на окнах.
относительные тайминги (не должны быть отключены более чем в 100 раз;)
Вызов функции - это просто сдвиг указателя кадра в памяти на стек и добавление к нему нового кадра. Параметры функции смещаются в локальные регистры для использования, и указатель стека продвигается к новой вершине стека для выполнения функции.
По сравнению со временем
Функциональный вызов ~ простой доступ к памяти
Вызов функции < Доступ к диску
Вызов функции < доступ к памяти на другом компьютере
Вызов функции < диск на другом компьютере
По сравнению с простым доступом к памяти - немного больше, практически неважно.
По сравнению со всеми остальными перечисленными - на порядок меньше.
Это должно быть справедливо для любого языка на любой ОС.
В общем случае вызов функции будет немного медленнее, чем доступ к памяти, поскольку на самом деле он должен выполнять несколько обращений к памяти для выполнения вызова. Например, для большинства вызовов функций требуется многократное нажатие и выборка стека, используя __stdcall на x86. Но если ваш доступ к памяти относится к странице, которая даже не находится в кеше L2, вызов функции может быть намного быстрее, если адресат и стек находятся в кэшах памяти процессора.
Во всем остальном вызов функции - это много (много) величин быстрее.
Трудно ответить, потому что есть много факторов.
Прежде всего, "Простой доступ к памяти" не прост. Поскольку на современных тактовых частотах CPU может добавить два числа быстрее, чем получить номер с одной стороны чипа на другой (скорость света - это не просто хорошая идея, это ЗАКОН)
Итак, называется ли функция внутри кэша памяти процессора? Доступ к памяти вы тоже сравниваете?
Затем мы вызываем вызов функции, чтобы очистить конвейер команд CPU, который будет влиять на скорость недетерминированным способом.
Предполагая, что вы имеете в виду накладные расходы самого вызова, а не то, что может сделать вызываемый, он определенно далеко, намного быстрее, чем все, кроме "простого" доступа к памяти.
Он, вероятно, медленнее, чем доступ к памяти, но обратите внимание, что, поскольку компилятор может выполнять inlining, накладные расходы функции иногда равны нулю. Даже если это не так, по крайней мере на некоторых архитектурах возможно, что некоторые вызовы кода уже в кэше команд могут быть быстрее, чем доступ к основной (нераспределенной) памяти. Это зависит от того, сколько регистров должно быть выложено перед тем, как сделать звонок, и что-то вроде этого. Обратитесь к документации по составлению компилятора и вызывающей конвенции, хотя вы вряд ли сможете это выяснить быстрее, чем разобрать испускаемый код.
Также обратите внимание, что "простой" доступ к памяти иногда не является - если ОС должна принести страницу с диска, тогда у вас есть длинное ожидание на ваших руках. То же самое было бы верно, если вы перейдете на код, который в настоящее время выгружается на диск.
Если основной вопрос: "Когда мне нужно оптимизировать мой код, чтобы свести к минимуму общее количество вызовов функций?", тогда ответ "очень близок к никогда".
В Google эта ссылка очень много. Для справки в будущем я провел короткую программу на С# по стоимости вызова функции, и ответ: "примерно в шесть раз больше стоимости встроенного". Ниже приведены подробности, см.//Выход внизу. ОБНОВЛЕНИЕ: Чтобы лучше сравнить яблоки с яблоками, я изменил Class1.Method, чтобы вернуть 'void', так: public void Method1() {//return 0; }
Тем не менее, inline быстрее на 2x: inline (avg): 610 мс; вызов функции (avg): 1380 мс. Поэтому ответ, обновленный, "примерно два раза".
с использованием System; используя System.Collections.Generic; используя System.Linq; используя System.Text; используя System.Diagnostics;
пространство имен FunctionCallCost { классная программа { static void Main (string [] args) { Debug.WriteLine( "STOP1" ); int iMax = 100000000;//100M DateTime funcCall1 = DateTime.Now; Секундомер sw = секундомер .StartNew();
for (int i = 0; i < iMax; i++)
{
//gives about 5.94 seconds to do a billion loops,
// or 0.594 for 100M, about 6 times faster than
//the method call.
}
sw.Stop();
long iE = sw.ElapsedMilliseconds;
Debug.WriteLine("elapsed time of main function (ms) is: " + iE.ToString());
Debug.WriteLine("stop2");
Class1 myClass1 = new Class1();
Stopwatch sw2 = Stopwatch.StartNew();
int dummyI;
for (int ie = 0; ie < iMax; ie++)
{
dummyI = myClass1.Method1();
}
sw2.Stop();
long iE2 = sw2.ElapsedMilliseconds;
Debug.WriteLine("elapsed time of helper class function (ms) is: " + iE2.ToString());
Debug.WriteLine("Hi3");
}
}
//Класс 1 здесь использование системы; используя System.Collections.Generic; используя System.Linq; используя System.Text;
пространство имен FunctionCallCost { класс Class1 {
public Class1()
{
}
public int Method1 ()
{
return 0;
}
}
}
//Выход: STOP1 прошедшее время основной функции (мс): 595 STOP2 прошедшее время функции вспомогательного класса (мс): 3780
STOP1 прошедшее время основной функции (мс): 592 STOP2 прошедшее время функции вспомогательного класса (ms): 4042
STOP1 прошедшее время основной функции (мс): 626 STOP2 прошедшее время функции вспомогательного класса (мс): 3755
Стоимость фактического вызова функции, но не полного ее выполнения? или стоимость фактического выполнения функции? просто настройка вызова функции - это не дорогостоящая операция (обновление ПК?). но, очевидно, стоимость выполнения функции полностью зависит от того, что делает функция.
Не забывайте, что С++ имеет виртуальные вызовы (значительно дороже, около x10), а на WIndows вы можете ожидать VS к встроенным вызовам (0 по определению, так как в двоичном коде нет вызова)
В зависимости от того, что делает эта функция, он будет падать 2-м в вашем списке, если бы он выполнял логику с объектами в памяти. Далее по списку, если он включает в себя доступ к диску/сети.
Вызов функции обычно включает в себя только пару копий памяти (часто в регистры, поэтому они не должны занимать много времени), а затем операцию перехода. Это будет медленнее, чем доступ к памяти, но быстрее, чем любые другие операции, упомянутые выше, потому что они требуют связи с другим оборудованием. То же самое должно быть справедливо для любой комбинации OS/language.
Если функция включена во время компиляции, стоимость функции становится эквивалентной 0.
0, конечно, то, что вы получили бы, не имея вызова функции, т.е. самостоятельно вложил его.
Это, конечно, звучит чрезмерно очевидно, когда я пишу это так.
Стоимость вызова функции зависит от архитектуры. x86 значительно медленнее (несколько часов с тактовой частотой и так далее для аргумента функции), в то время как 64-бит намного меньше, потому что большинство аргументов функции передаются в регистрах, а не в стеке.
Функциональный вызов на самом деле представляет собой копию параметров в стек (доступ к нескольким ячейкам памяти), сохранение регистра, фактическое выполнение кода и, наконец, восстановление копии и восстановление реестров (регистры сохранения/восстановления зависят от системы).
Итак, говоря относительно:
Только доступ к памяти быстрее, чем вызов функции.
Но вызов можно избежать, если компилятор с встроенной оптимизацией (для компилятора (ов) GCC и не только он активируется при использовании уровня 3 оптимизации (-O3)).