Scala Generic Type慢

时间:2018-03-07 10:59:38

标签: scala

我确实需要创建一个比较Int或String或Char的方法。使用AnyVal是不可能的,因为没有方法可用于<,>比较。

然而,将其键入Ordered会显示出明显的缓慢。有没有更好的方法来实现这一目标?计划是进行通用二进制排序,发现通用类型会降低性能。

sbt test

结果显示:

  

的Sample1:696122

     

样品2:45123

     

样品3:13947

     

样品3:5332

     

样品2:194438

     

的Sample1:497992

我是否采用不正确的方式处理泛型?或者我应该在这种情况下使用旧的Java方法来使用Comparator,样本如下:

def sample1[T <% Ordered[T]](x:T) = { x < (x) }
def sample2(x:Ordered[Int]) = { x < 1 }
def sample3(x:Int) = { x < 1 }

val start1 = System.nanoTime
sample1(5)
println(System.nanoTime - start1)
val start2 = System.nanoTime
sample2(5)
println(System.nanoTime - start2)
val start3 = System.nanoTime
sample3(5)
println(System.nanoTime - start3)
val start4 = System.nanoTime
sample3(5)
println(System.nanoTime - start4)
val start5 = System.nanoTime
sample2(5)
println(System.nanoTime - start5)
val start6 = System.nanoTime
sample1(5)
println(System.nanoTime - start6)

2 个答案:

答案 0 :(得分:2)

如果你想获得与生产环境相似的结果,不要以这种方式进行微观测试。 首先,你需要热身jvm。之后,将您的测试作为多次迭代的平均值。此外,由于const数据,您需要防止可能的jvm优化。 E.g。

def sample1[T <% Ordered[T]](x:T) = { x < (x) }
def sample2(x:Ordered[Int]) = { x < 1 }
def sample3(x:Int) = { x < 1 }
val r = new Random()


def measure(f: => Unit): Long = {
  val start1 = System.nanoTime
  f
  System.nanoTime - start1
}
val n = 1000000

(1 to n).map(_ => measure {val k = r.nextInt();sample1(k)})
(1 to n).map(_ => measure {val k = r.nextInt();sample2(k)})
(1 to n).map(_ => measure {val k = r.nextInt();sample3(k)})

val avg1 = (1 to n).map(_ => measure {val k = r.nextInt();sample1(k)}).sum / n
println(avg1)
val avg2 = (1 to n).map(_ => measure {val k = r.nextInt();sample2(k)}).sum / n
println(avg2)
val avg3 = (1 to n).map(_ => measure {val k = r.nextInt();sample3(k)}).sum / n
println(avg3)

我得到了结果,对我来说更有价值:

134
92
83

This本书可以为性能测试提供一些启示。

答案 1 :(得分:2)

Java Comparator的Scala等价物是Ordering。其中一个主要区别在于,如果您不手动提供,可以由编译器隐式注入合适的Ordering。默认情况下,对于ByteInt的任何子类,以及其他一些明显的FloatOrderedComparable和其他基元,都会执行此操作案例。

此外,Ordering为所有主要比较方法提供了扩展方法的方法定义,因此您可以编写以下内容:

import Ordering.Implicits._

def sample5[T : Ordering](a: T, b: T) = a < b

def run() = sample5(1, 2)

从Scala 2.12开始,这些扩展操作(即a < b)会调用临时对象Ordering#Ops中的包装,因此代码将慢于Comparator。在大多数实际案例中并不多,但如果你关心微观优化,那么仍然很重要。

但是您可以使用替代语法来定义隐式Ordering[T]参数并直接调用Ordering对象上的方法。 实际上,即使生成的以下两种方法的字节码也是相同的(除了第三个参数的类型,并且可能是相应的compare方法的实现):

def withOrdering[T](x: T, y: T)(implicit cmp: Ordering[T]) = {
  cmp.compare(x, y) // also supports other methods, like `cmp.lt(x, y)`
}

def withComparator[T](x: T, y: T, cmp: Comparator[T]) = {
  cmp.compare(x, y)
}

实际上,当使用Int参数调用这些方法时,我的机器上的运行时是相同的。

因此,如果您想在Scala中一般比较类型,通常应该使用Ordering