【问题标题】:Iteration vs Recursion efficiency迭代与递归效率
【发布时间】:2015-07-18 10:07:40
【问题描述】:

我对递归的工作原理有了一个基本的了解——但我一直在迭代地编程。

当我们查看关键字 CPU/stack/calls 和空间时,递归与迭代有何不同?

由于运行许多“堆栈(?)”,每个(最有可能)存储一个值,它需要更多内存。因此,与同一问题的迭代解决方案相比,它占用了更多的空间。这是一般的情况。在某些情况下递归会更好,例如编程河内塔等。

我错了吗?我马上就要考试了,我要准备很多科目。递归不是我的强项,所以我希望能在这件事上有所帮助:)

【问题讨论】:

  • 如果您以递归方式编程,编译器可能会让它为您迭代,但您的好处是您不必考虑将使用多少空间(因为堆栈是一个神奇的、无限的资源!)。如果您使用分段分配处理迭代中的未知空间问题,您可能会浪费与递归一样多的内存,并且您可能还会有堆管理 CPU 开销。另一方面,如果编译器不修复它,那么递归可能会导致一长串函数返回以展开堆栈。
  • @sh1 堆栈绝对不是“神奇的无限资源”。事实上,它通常是非常有限的......因此,深度递归容易出现“堆栈溢出”错误,这通常会突然终止程序......
  • @twalberg 我认为如果这不是不言而喻的,那么这个网站将不得不被称为别的东西。我只是嘲笑该技术缺乏错误检测(崩溃之外)。也就是说,您也无法检测到 malloc 的错误。只要您的程序不崩溃,所有内存都是神奇且无限的。
  • Sh1,如果我们想象编译器没有“使”它迭代,那么当涉及到 CPU、堆栈、调用和/或空间时,递归通常是如何/确切地起作用的?感谢您的 cmets - 我只是不太明白。
  • @sh1 这种心态有点像“我的银行里有无限的钱,至少在 Vito 带着他的 Louisville Slugger 停下来之前”......记忆不是神奇的,也不是无限的,假设它是其中之一导致一些真正可怕的编码实践。最好准确了解内存的工作原理及其限制,并学习如何在牢记这些限制的情况下正确编码。

标签: performance recursion stack cpu


【解决方案1】:

这实际上取决于语言和编译器/解释器的性质。

一些函数式语言实现尾递归,例如,识别堆栈帧可以在递归到下一个调用之前被销毁/释放的特定情况。在支持它的语言/编译器/解释器中的那些特殊情况下,您实际上可以进行无限递归而不会溢出堆栈。

如果您使用的语言使用硬件堆栈并且不实现尾递归,那么通常您必须在分支到函数之前将参数推送到堆栈并将它们与返回值一起弹出,所以有引擎盖下有点隐含的数据结构(如果我们可以这样称呼它的话)。这里还可能发生各种额外的事情,比如注册阴影来优化它。

硬件堆栈通常非常高效,通常只是递增和递减堆栈指针寄存器以进行推送和弹出,但它确实涉及比使用循环计数器或条件进行分支更多的状态和指令。也许更重要的是,它往往涉及更远的分支以跳转到另一个函数的代码,而不是在同一代码体中循环,这可能涉及更多的指令缓存和页面未命中。

在这些类型的语言/编译器/解释器中,它们使用硬件堆栈并且总是以足够的递归来溢出它,循环路由通常提供性能优势(但代码可能更乏味)。

作为一个转折点,有时您还拥有积极的优化器,它们在将代码转换为机器指令并将其链接(如内联函数和展开循环)的过程中对您的代码进行各种魔术,并且当您考虑所有这些因素时,通常最好只更自然地编写代码,然后使用分析器进行测量,如果它可以使用一些调整。当然你不应该在可能溢出的情况下使用递归,但我通常不会担心大部分时间的开销。

【讨论】:

    猜你喜欢
    • 2012-02-12
    • 2012-04-26
    • 2018-08-16
    • 2012-08-29
    • 2021-05-26
    • 1970-01-01
    • 2012-03-12
    • 2019-04-27
    • 2015-02-06
    相关资源
    最近更新 更多