我是新来的,如果我以错误的方式发帖,请道歉。
我想知道是否有人可以解释为什么C函数调用这么慢?
很容易对关于递归斐波纳契的标准问题给出一个浅薄的答案,但如果我尽可能深地了解“更深层次”的理由,我将不胜感激。
感谢。
Edit1:抱歉这个错误。我误解了维基上的一篇文章。
答案 0 :(得分:8)
当你进行函数调用时,你的程序必须在堆栈上放置几个寄存器,可能会推送更多东西,并弄乱堆栈指针。这就是所有可能“慢”的东西。实际上,这很快。在x86_64平台上大约有10个机器指令。
如果您的代码稀疏且功能非常小,那么速度很慢。这就是Fibonacci函数的情况。但是,您必须在“慢速调用”和“慢速算法”之间做出改变:使用递归实现计算Fibonacci套件几乎是最慢的直接方式。函数体中涉及的代码几乎与函数序言和结尾(推送和弹出发生的地方)相同。
在某些情况下,调用函数实际上会使代码整体更快。当您处理大型函数并且您的寄存器很拥挤时,编译器可能会花费大量时间来决定存储数据的寄存器。但是,在函数调用中隔离代码将简化编译器决定使用哪个寄存器的任务。
所以,不,C调用并不慢。
答案 1 :(得分:6)
根据您在评论中发布的其他信息,似乎令您困惑的是这句话:
“在语言(如C和Java)中 这有利于迭代循环 构造,通常有 显着的时间和空间成本 与递归程序相关联, 由于管理所需的开销 堆栈和相对缓慢的 函数调用;“
在递归实现斐波纳契计算的上下文中。
这就是说递归函数调用比循环慢但这并不意味着函数调用一般慢或者C中的函数调用是比其他语言的函数调用慢。
Fibbonacci生成自然是递归算法,因此最明显和自然的实现涉及许多函数调用,但也可以表示为迭代(循环)。
特别是斐波纳契数生成算法具有一个称为tail recursion的特殊属性。尾递归递归函数可以轻松自动地转换为迭代,即使它表示为递归函数。有些语言,特别是递归很常见且迭代很少的函数式语言,可以保证它们能够识别这种模式并自动将这种递归转换为“引擎盖下”的迭代。一些优化的C编译器也会这样做,但不能保证。在C中,由于迭代既是常见的又是惯用的,并且由于尾部递归优化不一定由编译器为您完成,因此最好将其显式地编写为迭代以实现最佳性能。
因此,相对于其他语言,将此引用解释为对C函数调用速度的评论,将苹果与橙子进行比较。所讨论的其他语言是可以采用某些函数调用模式的语言(这些模式恰好发生在fibbonnaci数字生成中)并自动将它们转换为更快但更快的因为它实际上不是函数调用所有
答案 2 :(得分:4)
C函数调用速度不慢。 调用C函数的开销非常低。
我挑战你提供证据支持你的断言。
答案 3 :(得分:3)
对于像递归计算Fibonacci数字这样的工作,C有几个原因可能比某些其他语言慢。事实上,它们与慢速函数调用都没有任何关系。
在很多函数式语言(以及或多或少的函数式常见的语言)中,递归(通常非常深递归)非常常见。为了保持速度合理,这些语言的许多实现都做了相当多的工作,优化递归调用(除其他外),在可能的情况下将它们转换为迭代。
相当多的人也会“记住”之前调用的结果 - 也就是说,他们会跟踪最近通过的许多值的函数结果。当/如果再次传递相同的值时,它们可以简单地返回适当的值而无需重新计算它。
然而,应该注意的是,这里的优化并不是更快的函数调用 - 它是避免(通常很多)函数调用。
答案 4 :(得分:2)
我不确定你对“递归斐波纳契标准问题的浅层答案”是什么意思。
朴素递归实现的问题不是函数调用很慢,而是你进行指数级大量的调用。通过缓存结果(memoization),您可以减少调用次数,从而允许算法以线性时间运行。
答案 5 :(得分:2)
Recursive Fibonacci
是原因,而不是C语言。递归Fibonacci就像
int f(int i)
{
return i < 2 ? 1 : f(i-1) + f(i-2);
}
这是计算斐波那契数的最慢算法,并且通过使用称为函数列表的堆栈存储 - &gt;让它变慢。
答案 6 :(得分:1)
在那些语言中,C可能是最快的(除非你是汇编语言程序员)。大多数C函数调用都是100%纯栈操作。当您调用函数时,在二进制代码中转换的含义是,CPU将您传递给函数的所有参数推送到堆栈中。然后,它调用该函数。然后该功能会弹出您的参数。之后,它会执行构成函数的任何代码。最后,任何返回参数都被压入堆栈,然后函数结束并弹出参数。任何CPU上的堆栈操作通常比其他任何操作都快。
如果您正在使用分析器或者说正在进行函数调用的东西很慢,那么它就是您函数内的代码。尝试在此处发布您的代码,我们将看到发生了什么。
答案 7 :(得分:0)
我不确定你的意思。 C基本上是CPU汇编指令之上的一个抽象层,速度非常快。 你应该真的澄清你的问题。
答案 8 :(得分:0)
在某些语言中,主要是函数范例,可以优化在函数体末端进行的函数调用,以便重用相同的堆栈帧。这可以节省时间和空间。当函数既短又递归时,好处尤其显着,因此堆栈开销可能会使实际工作相形见绌。
因此,天真的Fibonacci算法运行速度要快得多。 C通常不会执行此优化,因此其性能可能会受到影响。
但,正如已经说过的那样,斐波那契数字的朴素算法首先是极其低效的。无论是在C语言还是其他语言中,更高效的算法运行速度都要快得多。其他Fibonacci算法可能不会从所讨论的优化中看到几乎相同的好处。
因此,简而言之,在某些情况下,C通常不会支持某些可能导致显着性能提升的优化,但在大多数情况下,在这些情况下,您可以通过使用a来实现相同或更高的性能提升算法略有不同。
答案 9 :(得分:0)
我同意Mark Byers,因为你提到了递归斐波那契。尝试添加printf,以便每次添加时都会打印一条消息。您会看到递归的Fibonacci正在做更多的添加,它可能会在第一眼看出。
答案 10 :(得分:0)
文章所讨论的是递归和迭代之间的区别。
这是在计算机科学中名为算法分析的主题下。
假设我写了斐波那契函数,它看起来像这样:
//finds the nth fibonacci
int rec_fib(n) {
if(n == 1)
return 1;
else if (n == 2)
return 1;
else
return fib(n-1) + fib(n - 2)
}
如果你把它写在纸上(我推荐这个),你会看到这个金字塔形状出现。
正在接受一个完整的Lotta电话来完成工作。
然而,还有另一种写斐波纳契的方法(还有其他几种方法)
int fib(int n) //this one taken from scriptol.com, since it takes more thought to write it out.
{
int first = 0, second = 1;
int tmp;
while (n--)
{
tmp = first+second;
first = second;
second = tmp;
}
return first;
}
这个只占用与n成正比的时间长度,而不是你之前看到的在二维中成长的大金字塔形状。
通过算法分析,您可以确定运行时间与这两个函数中n的大小的增长速度。
此外,一些递归算法很快(或者可以被欺骗为更快)。这取决于算法 - 这就是算法分析重要且有用的原因。
这有意义吗?