递归 GO vs Scala

huangapple go评论137阅读模式
英文:

Recursive GO vs Scala

问题

以下是翻译好的内容:

以下是Scala代码,完成时间为1.5分钟,而GO中等效的代码完成时间为2.5分钟。
在fib(40)之前,两者都需要2秒。差距出现在fib(50)。
我有这样的印象,GO作为本地语言应该比Scala更快。

Scala优化?
Golang的限制?

正如“我的另一辆车是一辆卡车”所说的问题是“为什么Scala在这个特定的微基准测试中比GO更快?”
忘记斐波那契数列,假设我有一个需要递归的函数。
在递归情况下,Scala是否更优?

这可能是内部编译器实现,甚至是Scala特定的优化。
只有当你知道答案时,请回答。

GO中的循环在12秒内运行15000000000次。

以下是GO代码:

func fib(n int) (two int) {
    one := 0
    two = 1
    for i := 1; i != n; i++ {
        one, two = two, (one + two)
    }
    return
}
英文:

The following Scala code complete in 1.5 minutes while the equivalent code in GO finish in 2.5 minutes.
Up to fib(40) both take 2 sec. The gap appear in fib(50)
I got the impression the GO, being native, should be faster then Scala.

Scala

def fib(n:Int):Long = {
    n match {
    	case 0 => 0
	    case 1 => 1
	    case _ => fib(n-1) + fib(n-2)
    }
}

GO

func fib(n int) (ret int) {
    if n > 1 {
	    return fib(n-1) + fib(n-2)
    }
    return n
}

Scala optimization?
Golang limitation?

As "My other car is a cadr" said the question is "how come Scala is faster than GO in this particular microbenchmark?"

Forget the Fibonacci lets say I do have a function that require recursion.
Is Scala superior in recursion situations?

Its probably an internal compiler implementation or even Scala specific optimization.
Please answer just if you know.

Go in loop run 15000000000 in 12 sec

func fib(n int) (two int) {
    one := 0
    two = 1
    for i := 1; i != n; i++ {
        one, two = two, (one + two)
    }
    return
}

答案1

得分: 3

对于Go语言,应该使用迭代而不是递归。可以通过显式堆栈将递归替换为迭代。这样可以避免函数调用和调用栈管理的开销。例如,使用迭代并将n从50增加到1000几乎不需要时间:

package main

import "fmt"

func fib(n int) (f int64) {
    if n < 0 {
        n = 0
    }
    a, b := int64(0), int64(1)
    for i := 0; i < n; i++ {
        f = a
        a, b = b, a+b
    }
    return
}

func main() {
    n := 1000
    fmt.Println(n, fib(n))
}

输出:

$ time .fib
1000 8261794739546030242
real    0m0.001s
user    0m0.000s
sys     0m0.000s

使用适当的算法。避免指数时间复杂度。当性能重要时,不要使用递归来计算斐波那契数列。

参考资料:

Recursive Algorithms in Computer Science Courses: Fibonacci Numbers and Binomial Coefficients

我们观察到,几乎所有计算机科学课程的教科书在前三年的课程中都没有适当地涵盖分支递归函数的计算效率问题。斐波那契数列和二项式系数经常被用作分支递归函数的示例。然而,它们的指数时间复杂度很少被提及,也从未在教科书中完全证明。很少提到替代的线性时间迭代解法。我们给出了这些递归函数具有指数时间复杂度的非常简单的证明。

递归是一种对于只进行一次递归调用的定义和算法非常高效的技术,但如果进行两次或更多次递归调用,它可能非常低效。因此,递归方法通常更适用于概念工具而不是高效的计算工具。本文介绍的证明在渥太华大学的一年级学生中成功教授(五年时间)。建议在第一门计算机科学课程的第二部分涵盖递归作为问题解决和定义工具。然而,递归编程应该推迟到课程的最后(或者更好地在第二门计算机科学课程的开头),在熟练掌握迭代程序和理解堆栈操作之后。

英文:

For Go, use iteration not recursion. Recursion can be replaced by iteration with an explicit stack. It avoids the overhead of function calls and call stack management. For example, using iteration and increasing n from 50 to 1000 takes almost no time:

package main

import &quot;fmt&quot;

func fib(n int) (f int64) {
	if n &lt; 0 {
		n = 0
	}
	a, b := int64(0), int64(1)
	for i := 0; i &lt; n; i++ {
		f = a
		a, b = b, a+b
	}
	return
}

func main() {
	n := 1000
	fmt.Println(n, fib(n))
}

Output:

$ time .fib
1000 8261794739546030242
real	0m0.001s
user	0m0.000s
sys	0m0.000s

Use appropriate algorithms. Avoid exponential time complexity. Don't use recursion for Fibonacci numbers when performance is important.

Reference:

> Recursive Algorithms in Computer Science Courses: Fibonacci Numbers
> and Binomial Coefficients

>
> We observe that the computational inefficiency of branched recursive
> functions was not appropriately covered in almost all textbooks for
> computer science courses in the first three years of the curriculum.
> Fibonacci numbers and binomial coefficients were frequently used as
> examples of branched recursive functions. However, their exponential
> time complexity was rarely claimed and never completely proved in the
> textbooks. Alternative linear time iterative solutions were rarely
> mentioned. We give very simple proofs that these recursive functions
> have exponential time complexity.
>
> Recursion is an efficient technique for definitions and algorithms
> that make only one recursive call, but can be extremely inefficient if
> it makes two or more recursive calls. Thus the recursive approach is
> frequently more useful as a conceptual tool rather than as an
> efficient computational tool. The proofs presented in this paper were
> successfully taught (over a five-year period) to first year students
> at the University of Ottawa. It is suggested that recursion as a
> problem solving and defining tool be covered in the second part of the
> first computer science course. However, recursive programming should
> be postponed for the end of the course (or perhaps better at the
> beginning of the second computer science course), after iterative
> programs are well mastered and stack operation well understood.

答案2

得分: 0

Scala的解决方案会消耗堆栈,因为它不是尾递归(加法发生在递归调用之后),但它不应该创建任何垃圾。

很可能你正在使用的Hotspot编译器(可能是服务器版)对于这种代码模式来说比Go编译器更好。

如果你真的好奇,你可以下载一个调试版本的JVM,并让它打印出汇编代码

英文:

The Scala solution will consume stack, since it's not tail recursive (the addition happens after the recursive call), but it shouldn't be creating any garbage at all.

Most likely whichever Hotspot compiler you're using (probably server) is just a better compiler, for this code pattern, than the Go compiler.

If you're really curious, you can download a debug build of the JVM, and have it print out the assembly code.

huangapple
  • 本文由 发表于 2014年7月17日 13:08:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/24795294.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定