scala专门化 - 使用对象而不是类导致减速?

时间:2013-05-21 11:47:35

标签: scala jvm benchmarking scala-2.10 microbenchmark

我做了一些基准测试并且得到的结果我不知道如何解释。

简而言之:

我有2个类使用泛型数组执行相同(计算繁重)的操作,它们都使用特化(@specialized,后来@spec)。一个类被定义为:

class A [@spec T] {
  def a(d: Array[T], c: Whatever[T], ...) = ...
  ...
}

第二:(单身人士)

object B {
  def a[@spec T](d: Array[T], c: Whatever[T], ...) = ...
  ...
}

在第二种情况下,我获得了巨大的性能。为什么会这样? (注意:目前我不太了解Java字节码,而Scala编译器内部也是如此。)

更多详情:

完整代码在此处:https://github.com/magicgoose/trashbox/tree/master/sorting_tests/src/magicgoose/sorting 这是从Java中删除的排序算法,(几乎)自动转换为Scala,并且比较操作更改为通用的,以允许使用与原始类型的自定义比较而无需装箱。加上简单的基准测试(不同长度的测试,JVM预热和平均) 结果如下所示:(左列是原始Java Arrays.sort(int[])

     JavaSort                    |      JavaSortGen$mcI$sp          |      JavaSortGenSingleton$mcI$sp
length 2        | time 0.00003ms | length 2        | time 0.00004ms | length 2        | time 0.00006ms
length 3        | time 0.00003ms | length 3        | time 0.00005ms | length 3        | time 0.00011ms
length 4        | time 0.00005ms | length 4        | time 0.00006ms | length 4        | time 0.00017ms
length 6        | time 0.00008ms | length 6        | time 0.00010ms | length 6        | time 0.00036ms
length 9        | time 0.00013ms | length 9        | time 0.00015ms | length 9        | time 0.00069ms
length 13       | time 0.00022ms | length 13       | time 0.00028ms | length 13       | time 0.00135ms
length 19       | time 0.00037ms | length 19       | time 0.00040ms | length 19       | time 0.00245ms
length 28       | time 0.00072ms | length 28       | time 0.00060ms | length 28       | time 0.00490ms
length 42       | time 0.00127ms | length 42       | time 0.00096ms | length 42       | time 0.01018ms
length 63       | time 0.00173ms | length 63       | time 0.00179ms | length 63       | time 0.01052ms
length 94       | time 0.00280ms | length 94       | time 0.00280ms | length 94       | time 0.01522ms
length 141      | time 0.00458ms | length 141      | time 0.00479ms | length 141      | time 0.02376ms
length 211      | time 0.00731ms | length 211      | time 0.00763ms | length 211      | time 0.03648ms
length 316      | time 0.01310ms | length 316      | time 0.01436ms | length 316      | time 0.06333ms
length 474      | time 0.02116ms | length 474      | time 0.02158ms | length 474      | time 0.09121ms
length 711      | time 0.03250ms | length 711      | time 0.03387ms | length 711      | time 0.14341ms
length 1066     | time 0.05099ms | length 1066     | time 0.05305ms | length 1066     | time 0.21971ms
length 1599     | time 0.08040ms | length 1599     | time 0.08349ms | length 1599     | time 0.33692ms
length 2398     | time 0.12971ms | length 2398     | time 0.13084ms | length 2398     | time 0.51396ms
length 3597     | time 0.20300ms | length 3597     | time 0.20893ms | length 3597     | time 0.79176ms
length 5395     | time 0.32087ms | length 5395     | time 0.32491ms | length 5395     | time 1.30021ms

后者是在object内定义的那个,它很糟糕(大约慢4倍)。

更新1

我使用和不使用scalac optimise选项运行基准测试,并且没有明显的差异(仅使用optimise进行较慢的编译)。

1 个答案:

答案 0 :(得分:6)

这只是专业化中的许多错误之一 - 我不确定是否已经在错误跟踪器上报告了这个错误。如果你从你的排序中抛出异常,你会发现它调用泛型版本而不是第二种类型的专用版本:

java.lang.Exception: Boom!
    at magicgoose.sorting.DualPivotQuicksortGenSingleton$.magicgoose$sorting$DualPivotQuicksortGenSingleton$$sort(DualPivotQuicksortGenSingleton.scala:33)
    at magicgoose.sorting.DualPivotQuicksortGenSingleton$.sort$mFc$sp(DualPivotQuicksortGenSingleton.scala:13)

请注意堆栈顶部的内容是DualPivotQuicksortGenSingleton$$sort(...)而不是...sort$mFc$sp(...)?编译错误,不好!

作为一种变通方法,您可以将私有方法包装在最终的帮助程序对象中,例如

def sort[@ spec T](a: Array[T]) { Helper.sort(a,0,a.length) }
private final object Helper {
  def sort[@spec T](a: Array[T], i0: Int, i1: Int) { ... }
}

无论出于何种原因,编译器都会意识到它应该调用专用变量。我还没有测试是否有另一个专用方法需要在其自己的对象中;我会通过抛出异常的技巧把它留给你。