改善特定Scala代码的性能

时间:2018-09-13 14:07:38

标签: scala performance

我需要使以下代码更高效。运行时间太长。

       def aFib(n: Int): BigInt = {
         var nn:   Int = n
         var sign: Int = 0
          if (nn < 0) {
              nn = -nn
              sign = 2 * (nn % 2) - 1
          } else {
              sign = 1
          }
          var a: BigInt = 1
          var b: BigInt = 0
          var p: BigInt = 0
          var q: BigInt = 1
          while (nn != 0) {
            if (nn % 2 == 1) {
                var t  = (b + a) * q + a * p 
                b = b * p + a * q 
                a = t
            }
            var t = p * p + q * q
            q =(2 * p * q) + q * q
            p = t
            nn /= 2
          }
          sign * b
    }

我已经尝试了不同的方法(迭代,递归等),并决定使用代码中包含的算法。 Cognoscenti将认识到它是计算正负斐波纳契数的一种众所周知的方法。我自己编写了代码,然后放入BigInt。似乎没有快速可用的快速实现。 由于Scala是一门复杂的语言,并且我的经验有限,因此我的直觉是有一些方法可以使代码更好-减少运行时间。欢迎所有建议。

2 个答案:

答案 0 :(得分:1)

首先,查看以下内容进行分析:https://developer.lightbend.com/blog/2018-04-09-profiling-JVM-applications/;其次,BigInt包装了java.math.BigInteger:https://github.com/scala/scala/blob/v2.12.3/src/library/scala/math/BigInt.scala#L1,这是一个任意精度的整数:https://docs.oracle.com/javase/7/docs/api/java/math/BigInteger.html

因此,留在Scala中可能最好的办法是切换到本机整数类型,并且仅在数字长久变得太大时才使用此特定代码。

在这段代码中,function可能更好地表示为位移。

最好的办法可能是使用数值计算库,并尽可能表达这些是矩阵计算,可以在gpu上并行执行:https://github.com/scalanlp/breeze

Update2:看起来Breeze似乎并没有以任何方式参与GPU:(

更新:最大的一次胜利可能是记住您的结果,因此您可以从先前的值(如果已经计算出)中计算出fibonnacci。存储这些结果,在表中查找它们,然后按照@tim的建议,在表中填充前几千个数字。

答案 1 :(得分:0)

使用惰性实现

import scala.math.BigInt
lazy val fibs: Stream[BigInt] = BigInt(0) #:: BigInt(1) #:: fibs.zip(fibs.tail).map { n => n._1 + n._2 }

fibs.take(5).last

@弗雷德 另外,您可以对负值和正值使用类似的方法

  def fib(value: Int): BigInt = {
  @tailrec
  def helper(cur: BigInt, prev: BigInt, n: BigInt): BigInt = {
    val zero = BigInt(0)
    if (n == zero) cur
    else if (n > 0) helper(cur + prev, cur, n - 1)
    else helper(prev, cur - prev, n + 1)
  }
  helper(0, 1, value)
}