Scala缓存是否转换为功能接口

Does scala cache conversions to functional interfaces

Scala 2.12 可以自动将 lambda 表达式转换为接口。例如,我正在使用:

import org.apache.kafka.common.serialization.{Deserializer, Serde, Serializer}
import scalapb.GeneratedMessageCompanion

class ProtoSerde[A <: scalapb.GeneratedMessage](implicit companion: GeneratedMessageCompanion[A])
  extends Serde[A] {
  override def serializer(): Serializer[A] = (_: String, data: A) => data.toByteArray

  override def deserializer(): Deserializer[A] = (_: String, data: Array[Byte]) => companion.parseFrom(data)
}

它是否会自动缓存创建对象的实例,以便不会在每次调用时都分配一个新对象。 IE。我要:

import org.apache.kafka.common.serialization.{Deserializer, Serde, Serializer}
import scalapb.GeneratedMessageCompanion

class ProtoSerde[A <: scalapb.GeneratedMessage](implicit companion: GeneratedMessageCompanion[A])
  extends Serde[A] {
  lazy val _serializer: Serializer[A] = (_: String, data: A) => data.toByteArray
  lazy val _deserializer: Deserializer[A] = (_: String, data: Array[Byte]) => companion.parseFrom(data)

  override def serializer(): Serializer[A] = _serializer

  override def deserializer(): Deserializer[A] = _deserializer
}

编译器会自动执行此优化,还是我必须自己执行?

编译器不执行此类优化。将 lambda 表达式转换为接口并不意味着将该接口的实例保存为单例或任何其他保存方式。每次调用此 lambda 时,它都会创建此实例。你需要自己做这个优化。

def 没有记住调用它每次都会重新计算值。稍后可能会被 JIT 编译器优化。

但是有一个更简单的解决方案可以使 class 的属性记忆化。 Scala 允许在覆盖父方法时将 def 更改为 val。也可以在扩展 class:

时添加 lazy 修饰符
class ProtoSerde[A <: scalapb.GeneratedMessage](implicit companion: GeneratedMessageCompanion[A])
  extends Serde[A] {
  override lazy val serializer: Serializer[A] = (_: String, data: A) => data.toByteArray

  override lazy val deserializer: Deserializer[A] = (_: String, data: Array[Byte]) => companion.parseFrom(data)
}

也许最好在您的特定情况下进行基准测试。例如,以下简化代码段的 jmh 基准测试

trait Quxable[A] {
  def qux(x: A): A
}

class FooImpl1 {
  def zar(): Quxable[Int] = (x: Int) => x + 1
}

class FooImpl2 {
  val _zar: Quxable[Int] = (x: Int) => x + 1
  def zar(): Quxable[Int] = _zar
}

// sbt "jmh:run -i 5 -wi 5 -f 2 -t 1 -prof gc bench.So61983239"

@State(Scope.Benchmark)
@BenchmarkMode(Array(Mode.Throughput))
class So61983239 {
  val end = 1000000
  val fooImpl1 = new FooImpl1
  val fooImpl2 = new FooImpl2

  @Benchmark def impl1 = (1 to end) map (x => fooImpl1.zar().qux(x))
  @Benchmark def impl2 = (1 to end) map (x => fooImpl2.zar().qux(x))
}

两者的分配率相似 (gc.alloc.rate)

[info] Benchmark                                           Mode  Cnt         Score         Error   Units
[info] So61983239.impl1                                   thrpt   10        76.866 ±       2.690   ops/s
[info] So61983239.impl1:·gc.alloc.rate                    thrpt   10      4790.899 ±     167.414  MB/sec
[info] So61983239.impl1:·gc.alloc.rate.norm               thrpt   10  68637392.621 ±       0.152    B/op
[info] So61983239.impl1:·gc.churn.PS_Eden_Space           thrpt   10      4793.715 ±     174.955  MB/sec
[info] So61983239.impl1:·gc.churn.PS_Eden_Space.norm      thrpt   10  68686591.527 ± 1866568.380    B/op
[info] So61983239.impl1:·gc.churn.PS_Survivor_Space       thrpt   10         6.216 ±       1.411  MB/sec
[info] So61983239.impl1:·gc.churn.PS_Survivor_Space.norm  thrpt   10     89091.175 ±   20368.810    B/op
[info] So61983239.impl1:·gc.count                         thrpt   10       234.000                counts
[info] So61983239.impl1:·gc.time                          thrpt   10      1055.000                    ms
[info] So61983239.impl2                                   thrpt   10        78.027 ±       0.889   ops/s
[info] So61983239.impl2:·gc.alloc.rate                    thrpt   10      4862.226 ±      54.507  MB/sec
[info] So61983239.impl2:·gc.alloc.rate.norm               thrpt   10  68637392.613 ±       0.162    B/op
[info] So61983239.impl2:·gc.churn.PS_Eden_Space           thrpt   10      4895.604 ±     148.716  MB/sec
[info] So61983239.impl2:·gc.churn.PS_Eden_Space.norm      thrpt   10  69105653.917 ± 1668136.154    B/op
[info] So61983239.impl2:·gc.churn.PS_Survivor_Space       thrpt   10         5.582 ±       2.634  MB/sec
[info] So61983239.impl2:·gc.churn.PS_Survivor_Space.norm  thrpt   10     78851.978 ±   37413.327    B/op
[info] So61983239.impl2:·gc.count                         thrpt   10       230.000                counts
[info] So61983239.impl2:·gc.time                          thrpt   10      1073.000                    ms