使用@specialized(Int)的Int和BigInt的Scala通用数学函数

时间:2016-12-16 08:29:12

标签: performance scala generics

我是scala的新手,我想知道是否可以定义适用于BigIntInt的通用数学函数,并且在Int的情况下可以定义函数的参数将被视为基元(在函数体中没有任何装箱和拆箱)。

所以,例如我可以做类似

的事情
def foo[@specialized(Int) T: Numeric](a: T, b: T) = {
  val n = implicitly[Numeric[T]]
  import n._
  //some code with the use of operators '+-*/'
  a * b - a * a + b * b * b
}

//works for primitive Int
val i1 : Int = 1
val i2 : Int = 2
val i3 : Int = foo(i1, i2)

//works for BigInt
val b1 : BigInt = BigInt(1)
val b2 : BigInt = BigInt(2)
val b3 : BigInt = foo(b1, b2)

这里foo我可以使用原始intsBigInts的所有数学运算符(这就是我需要的)。但是,函数foo(Int, Int)编译为以下内容:

 public int foo$mIc$sp(int a, int b, Numeric<Object> evidence$1) {
        Numeric n = (Numeric)Predef..MODULE$.implicitly(evidence$1);
        return BoxesRunTime.unboxToInt((Object)n.mkNumericOps(n.mkNumericOps(n.mkNumericOps((Object)BoxesRunTime.boxToInteger((int)a)).$times((Object)BoxesRunTime.boxToInteger((int)b))).$minus(n.mkNumericOps((Object)BoxesRunTime.boxToInteger((int)a)).$times((Object)BoxesRunTime.boxToInteger((int)a)))).$plus(n.mkNumericOps(n.mkNumericOps((Object)BoxesRunTime.boxToInteger((int)b)).$times((Object)BoxesRunTime.boxToInteger((int)b))).$times((Object)BoxesRunTime.boxToInteger((int)b))));
}

而不是简单:

//this is what I really need and expect from `@specialized(Int)`
public int foo$mIc$sp(int a, int b) {
   return a * b - a * a + b * b * b;
}

这会使@specialized(Int)无效,因为所有这些(联合国)拳击和不必要的调用n.mkNumericOps(...)的表现都低得令人无法接受。

那么,有没有办法实现像foo这样的泛型函数,它将编译为原始类型的“原样”代码?

1 个答案:

答案 0 :(得分:4)

问题是Numeric类型类没有专门化。

如果您想要高性能的通用数学,我强烈推荐spire数学库。

它有一个非常复杂的数学类型类层次结构,而不仅仅是数字。

以下是使用spire的示例:

import spire.implicits._ // typeclass instances etc.
import spire.syntax._    // syntax such as +-*/
import spire.algebra._   // typeclassses such as Field

def foo[@specialized T: Field](a: T, b: T) = {
  //some code with the use of operators '+-*/'
  a * b - a * a + b * b * b
}

您在这里说T必须有Field个实例。字段是指algebraic concept

Spire是高度模块化的:

  • spire.algebra 包含许多众所周知的代数概念,例如组,字段等,编码为scala类型

  • spire.syntax 包含用于向类型类实例可用的类型添加运算符和其他语法的隐式转换

  • spire.implicits 包含spire.algebra中类型类的实例,用于常见类型,如JVM基元。

这就是你需要三个进口的原因。

关于性能:如果您的代码是专用的,并且您使用的是基元,那么性能将与直接使用基元完全相同。

这是专门用于Int:

的foo方法的代码
public int foo$mIc$sp(int, int, spire.algebra.Field<java.lang.Object>);
  Code:
     0: aload_3
     1: aload_3
     2: aload_3
     3: iload_1
     4: iload_2
     5: invokeinterface #116,  3          // InterfaceMethod spire/algebra/Field.times$mcI$sp:(II)I
    10: aload_3
    11: iload_1
    12: iload_1
    13: invokeinterface #116,  3          // InterfaceMethod spire/algebra/Field.times$mcI$sp:(II)I
    18: invokeinterface #119,  3          // InterfaceMethod spire/algebra/Field.minus$mcI$sp:(II)I
    23: aload_3
    24: aload_3
    25: iload_2
    26: iload_2
    27: invokeinterface #116,  3          // InterfaceMethod spire/algebra/Field.times$mcI$sp:(II)I
    32: iload_2
    33: invokeinterface #116,  3          // InterfaceMethod spire/algebra/Field.times$mcI$sp:(II)I
    38: invokeinterface #122,  3          // InterfaceMethod spire/algebra/Field.plus$mcI$sp:(II)I
    43: ireturn

请注意,没有装箱,并且JVM会内联调用invokeinterface调用。