使用无形状HList时减少编译时间

Che*_*tah 4 scala shapeless

Scala 2.12.6,无形2.3.3

我有很多很深的大型模型(案例类)。我使用shapeless来帮助使用/操纵这些模型,还使用诸如circe之类的库,这些库大量使用了shapeless。

这导致我的编译时间在phase typerscalac 期间大大增加。

基于一点谷歌搜索,看起来无形是罪魁祸首,但是我似乎找不到任何有关如何改进它的具体技巧。

有人建议,因为我要为同一模型多次解析HList隐式(由于有多个库),所以我应该“缓存”它们-但是我不确定如何确切地找出要缓存的内容。

给出类似的东西:

case class MyModel(value: String) extends AnyVal
case class MyOtherModel(value: Int) extends AnyVal
case class MyRootModel(myModel: MyModel, myOtherModel: MyOtherModel)
Run Code Online (Sandbox Code Playgroud)

我应该为MyModel/ MyOtherModel和缓存什么MyRootModel

Tra*_*own 9

您可以LabelledGeneric像这样缓存Shapeless的实例:

import shapeless.{LabelledGeneric, the}

case class MyModel(value: String) extends AnyVal
case class MyOtherModel(value: Int) extends AnyVal
case class MyRootModel(myModel: MyModel, myOtherModel: MyOtherModel)

object MyModel {
  implicit val generic = the[LabelledGeneric[MyModel]]
}

object MyOtherModel {
  implicit val generic = the[LabelledGeneric[MyOtherModel]]
}

object MyRootModel {
  implicit val generic = the[LabelledGeneric[MyRootModel]]
}
Run Code Online (Sandbox Code Playgroud)

您当然应该看看这是否会缩短您自己项目中的编译时间,但是作为快速的基准测试,我们可以设置一个测试来解决LabelledGeneric重复的问题(在这种情况下为一千次):

object Test {
  def foo0 = {
    LabelledGeneric[MyRootModel]
    LabelledGeneric[MyRootModel]
    // repeat 98 more times...
  }
  def foo1 = {
    LabelledGeneric[MyRootModel]
    LabelledGeneric[MyRootModel]
    // repeat 98 more times...
  }
  // and so on through foo9 
}
Run Code Online (Sandbox Code Playgroud)

(请注意,我们必须对调用进行拆分,因为如果仅在单个方法中连续转储一千个调用,则在注释掉要比较的实例缓存时,宏生成的代码将超过JVM的方法大小限制。)

在我的机器上,Test.scala包含Test,案例类定义和缓存实例的文件将在3秒钟左右编译完毕。如果我们注释掉这些generic定义,则将花费12秒钟以上。这当然是不科学的,但令人鼓舞。

请注意,通常来说,implicit没有类型注释的定义不是一个好主意,您可以通过编写以下代码来避免对缓存的实例执行此操作:

import shapeless.{LabelledGeneric, TypeOf, cachedImplicit}

case class MyModel(value: String) extends AnyVal
case class MyOtherModel(value: Int) extends AnyVal
case class MyRootModel(myModel: MyModel, myOtherModel: MyOtherModel)

object MyModel {
  implicit val generic: TypeOf.`LabelledGeneric[MyModel]`.type = cachedImplicit
}

object MyOtherModel {
  implicit val generic: TypeOf.`LabelledGeneric[MyOtherModel]`.type = cachedImplicit
}

object MyRootModel {
  implicit val generic: TypeOf.`LabelledGeneric[MyRootModel]`.type = cachedImplicit
}
Run Code Online (Sandbox Code Playgroud)

TypeOf但是,这是一些奇怪的魔术,老实说,当我需要类似这样的东西时,我只是使用了这种the方法。

作为一个脚注,由于您特别提到了circe,因此您可能想尝试一下circe派生。它可以代替circe-generic的大部分功能,但它不是基于Shapeless构建的,编译速度更快。