scala - 减少使用 shapeless HList 时的编译时间

标签 scala shapeless

斯卡拉 2.12.6,无形 2.3.3。

我有很多非常深的大型模型(案例类)。我使用 shapeless 来帮助使用/操作这些模型,并且还使用像 circe 这样大量使用 shapeless 的库。

这导致我的编译时间在 phase typer 期间大量增加。 scalac 的一部分。

基于一些谷歌搜索,看起来无形是罪魁祸首,但我似乎无法找到任何关于如何改进这一点的具体提示。

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

给出类似的东西:

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

我应该缓存什么 MyModel/MyOtherModelMyRootModel ?

最佳答案

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

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]]
}

您当然应该看看这是否可以改善您自己项目中的编译时间,但作为快速基准,我们可以设置一个测试来解决 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 
}

(请注意,我们必须拆分调用,因为如果我们在单个方法中连续转储一千个调用,当我们注释掉要比较的实例缓存时,宏生成的代码将超过 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
}
TypeOf不过,这是一些奇怪的魔法,老实说,当我需要这样的东西时,我刚刚使用了 the方法。

作为脚注,由于您特别提到了 circe,您可能想要提供 circe-derivation一试。它可以直接替代 circe-generic 的大部分功能,但不是基于 Shapeless 构建的,并且编译速度更快。

关于scala - 减少使用 shapeless HList 时的编译时间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56233806/

相关文章:

scala - 有没有办法通过单个 HList 定义多个隐式证据?

scala - 固定大小和元素边界的 Nat 列表

scala - 在 scala shapeless 中,是否可以将文字类型用作泛型类型参数?

scala - 使用 Akka Futures 并行生成随机数

scala - HList 的无形状类型推断不起作用

scala - scala 如何支持这种 sbt DSL 语法?

scala - 无形的 HList 附加器

java - cuMemcpyDtoH 产生 CUDA_ERROR_INVALID_VALUE

scala - 如何在 Scala 中解构字符串长度(降序)的排序方法?

scala - Scala中的映射类型