避免在功能内装箱/拆箱 [英] Avoiding boxing/unboxing within function

查看:118
本文介绍了避免在功能内装箱/拆箱的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于数字密集型代码,我编写了具有以下签名的函数:

For a numeric intensive code I have written a function with the following signature:

def update( f: (Int,Int,Double) => Double ): Unit = {...}

但是,由于Function3不是专门的,因此f的每个应用程序都会对3个参数和结果类型进行装箱/拆箱.

However, because Function3 is not specialized, every application of f results in boxing/unboxing the 3 arguments and the result type.

我可以使用特殊的更新程序类:

I could use a special updater class:

trait Updater {
  def apply( i: Int, j: Int, x: Double ): Double
}
def update( f: Updater ): Unit = {...}

但是调用很麻烦(和java-ish一样):

But the invocation is cumbersome (and java-ish):

//with function
b.update( (i,j,_) => if( i==0 || j ==0 ) 1.0 else 0.5 )

//with updater
b.update( new Updater {
  def apply( i: Int, j: Int, x: Double ) = if( i==0 || j ==0 ) 1.0 else 0.5
} )

有没有办法在仍使用lambda语法的情况下避免装箱/拆箱?我希望宏会有所帮助,但我找不到任何解决方案.

Is there a way to avoid boxing/unboxing while still using the lambda syntax ? I was hoping macros will help, but I cannot figure any solution.

编辑:我用javap分析了function3生成的字节码.编译器会沿着通用方法生成未装箱的方法(请参见下文).有没有办法直接给拆箱的人打电话?

I analyzed the function3 generated byte code with javap. An unboxed method is generated by the compiler, along the generic method (see below). Is there a way to call the unboxed one directly ?

public final double apply(int, int, double);
  Code:
   0:   ldc2_w  #14; //double 100.0d
   3:   iload_2
   4:   i2d
   5:   dmul
   6:   iload_1
   7:   i2d
   8:   ddiv
   9:   dreturn

public final java.lang.Object apply(java.lang.Object, java.lang.Object, java.lang.Object);
  Code:
   0:   aload_0
   1:   aload_1
   2:   invokestatic    #31; //Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I
   5:   aload_2
   6:   invokestatic    #31; //Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I
   9:   aload_3
   10:  invokestatic    #35; //Method scala/runtime/BoxesRunTime.unboxToDouble:(Ljava/lang/Object;)D
   13:  invokevirtual   #37; //Method apply:(IID)D
   16:  invokestatic    #41; //Method scala/runtime/BoxesRunTime.boxToDouble:(D)Ljava/lang/Double;
   19:  areturn

推荐答案

由于您提到宏是一种可能的解决方案,所以我想到了编写一个带有匿名函数的宏的想法,提取应用方法并将其插入到匿名中扩展了名为F3的自定义函数特征的类.这是相当长的实现.

Since you mentioned macros as a possible solution, I got the idea of writing a macro that takes an anonymous function, extracts the apply methods and inserts it into an anonymous class that extends a custom function trait called F3. This is the quite long implementation.

特质F3

trait F3[@specialized A, @specialized B, @specialized C, @specialized D] {
  def apply(a:A, b:B, c:C):D
}

  implicit def function3toF3[A,B,C,D](f:Function3[A,B,C,D]):F3[A,B,C,D] = macro impl[A,B,C,D]

  def impl[A,B,C,D](c:Context)(f:c.Expr[Function3[A,B,C,D]]):c.Expr[F3[A,B,C,D]] = {
    import c.universe._
    var Function(args,body) = f.tree
    args = args.map(c.resetAllAttrs(_).asInstanceOf[ValDef])
    body = c.resetAllAttrs(body)
    val res = 
      Block(
        List(
          ClassDef(
            Modifiers(Flag.FINAL),
            newTypeName("$anon"),
            List(),
            Template(
              List(
                AppliedTypeTree(Ident(c.mirror.staticClass("mcro.F3")),
                  List(
                    Ident(c.mirror.staticClass("scala.Int")),
                    Ident(c.mirror.staticClass("scala.Int")),
                    Ident(c.mirror.staticClass("scala.Double")),
                    Ident(c.mirror.staticClass("scala.Double"))
                  )
                )
              ),
              emptyValDef,
              List(
                DefDef(
                  Modifiers(),
                  nme.CONSTRUCTOR,
                  List(),
                  List(
                    List()
                  ),
                  TypeTree(),
                  Block(
                    List(
                      Apply(
                        Select(Super(This(newTypeName("")), newTypeName("")), newTermName("<init>")),
                        List()
                      )
                    ),
                    Literal(Constant(()))
                  )
                ),
                DefDef(
                  Modifiers(Flag.OVERRIDE),
                  newTermName("apply"),
                  List(),
                  List(args),
                  TypeTree(),
                  body
                )
              )
            )
          )
        ),
        Apply(
          Select(
            New(
              Ident(newTypeName("$anon"))
            ),
            nme.CONSTRUCTOR
          ),
          List()
        )
      )




    c.Expr[F3[A,B,C,D]](res)
  }

由于我将宏定义为隐式的,因此可以像这样使用它:

Since I defined the macro as implicit, it can be used like this:

def foo(f:F3[Int,Int,Double,Double]) = {
  println(f.apply(1,2,3))
}

foo((a:Int,b:Int,c:Double)=>a+b+c)

在调用foo之前,将调用宏,因为foo需要F3的实例.正如预期的那样,对foo的调用将显示"6.0".现在,让我们看一下foo方法的反汇编,以确保没有装箱/拆箱:

Before foo is called, the macro is invoked because foo expects an instance of F3. As expected, the call to foo prints "6.0". Now let's look at the disassembly of the foo method, to make sure that no boxing/unboxing takes place:

public void foo(mcro.F3);
  Code:
   Stack=6, Locals=2, Args_size=2
   0:   getstatic       #19; //Field scala/Predef$.MODULE$:Lscala/Predef$;
   3:   aload_1
   4:   iconst_1
   5:   iconst_2
   6:   ldc2_w  #20; //double 3.0d
   9:   invokeinterface #27,  5; //InterfaceMethod mcro/F3.apply$mcIIDD$sp:(IID)D
   14:  invokestatic    #33; //Method scala/runtime/BoxesRunTime.boxToDouble:(D)Ljava/lang/Double;
   17:  invokevirtual   #37; //Method scala/Predef$.println:(Ljava/lang/Object;)V
   20:  return

这里唯一的装箱是对println的调用.是的!

The only boxing that is done here is for the call to println. Yay!

最后一句话:在当前状态下,该宏仅适用于Int,Int,Double,Double的特殊情况,但是可以很容易地对其进行修复.我把它留给读者练习.

One last remark: In its current state, the macro only works for the special case of Int,Int,Double,Double but that can easily be fixed. I leave that as an exercise to the reader.

这篇关于避免在功能内装箱/拆箱的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆