簡體   English   中英

Scala枚舉映射實現

[英]Scala enumeration map implementation

Java 有EnumMap - 專為鍵是enum類型的情況而設計的特殊Map實現(請參閱類聲明EnumMap<K extends Enum<K>,V> )。 例如, java.util.HashMap的主要優勢是內存和性能效率,因為它提前知道鍵的數量,因為它們是在枚舉中聲明的,所以它內存緊湊且速度更快,因為不需要擴展內部表並解決散列沖突。

我想知道這個 Java 實現是否有任何開箱即用的 Scala 實現,但是有 Scala 的Enumerationimmutable.Map接口。 也許任何外部庫實現或任何方式來創建自己的實現與接近內存和性能結果?

非常感謝您提前提供幫助!

Enumeratum 的公開問題EnumSet/EnumMap #113提供了一個信息基准

[info] MapComparisons.enumeratumScalaMapGet       avgt   30   9.830 ± 0.207  ns/op
[info] MapComparisons.jEnumEnumMapGet             avgt   30   4.745 ± 0.685  ns/op
[info] MapComparisons.jEnumScalaMapGet            avgt   30  12.204 ± 0.186  ns/op

勞埃德梅塔

所以目前的情況是 Java EnumMap 和 EnumSet 比普通的舊 Scala Set 和帶有 Enumeratum 的 Maps 快大約 2 倍,但由於我們仍處於 10 納秒以下的范圍內,因此可以說這里的性能獲勝似乎是合理的除非在超級邊緣情況下,否則不會成為瓶頸。

我嘗試復制 lloydmeta 的基准測試並使用 Scala 2.13 enumeratum 1.5.15 得到以下結果

sbt "jmh:run -i 10 -wi 10 -f 2 -t 1 -prof gc bench.So60374058"

平均時間,時間/操作

enumeratumScalaMapGet   avgt   20     8.284 ±   0.071   ns/op
jEnumEnumMapGet         avgt   20     2.883 ±   0.023   ns/op
jEnumScalaMapGet        avgt   20     7.361 ±   0.273   ns/op
vanillaScalaMapGet      avgt   20     6.650 ±   0.323   ns/op

內存分配率

enumeratumScalaMapGet:·gc.alloc.rate   avgt   20  1753.262 ±  14.548  MB/sec
jEnumEnumMapGet:·gc.alloc.rate         avgt   20    ≈ 10⁻⁴            MB/sec
jEnumScalaMapGet:·gc.alloc.rate        avgt   20  1976.491 ±  70.259  MB/sec
vanillaScalaMapGet:·gc.alloc.rate      avgt   20  2190.644 ± 105.208  MB/sec

我們注意到jEnumScalaMapGet自 2017 年以來似乎已經趕上了enumeratumScalaMapGet ,以及jEnumEnumMapGet的微不足道的內存分配。

基准源:

public enum JAgeGroup {
    Baby,
    Toddler,
    Teenager,
    Adult,
    Senior
}

import java.util
import java.util.concurrent.TimeUnit

import example.JAgeGroup
import org.openjdk.jmh.annotations._
import org.openjdk.jmh.infra.Blackhole
import enumeratum._

object AgeGroupVanilla extends Enumeration {
  type AgeGroupVanilla = Value
  val Baby, Toddler, Teenager, Adult, Senior = Value
}

sealed trait AgeGroup extends EnumEntry

case object AgeGroup extends Enum[AgeGroup] {

  val values = findValues

  case object Baby     extends AgeGroup
  case object Toddler  extends AgeGroup
  case object Teenager extends AgeGroup
  case object Adult    extends AgeGroup
  case object Senior   extends AgeGroup

}

@State(Scope.Thread)
@BenchmarkMode(Array(Mode.AverageTime))
@OutputTimeUnit(TimeUnit.NANOSECONDS)
class So60374058 {
  private val jEnumEnumMap = {
    val m: util.EnumMap[JAgeGroup, String] = new util.EnumMap(classOf[JAgeGroup])
    JAgeGroup.values().foreach(e => m.put(e, e.name()))
    m
  }

  private val jEnumScalaMap = Map(JAgeGroup.values().map(e => e -> e.name()): _*)

  private val ageGroupScalaMap = Map(AgeGroup.values.map(e => e -> e.entryName): _*)

  private val vanillaScalaMap = AgeGroupVanilla.values.map(e => e -> e.toString).toMap

  private def randomFrom[A](seq: Seq[A]): A = {
    seq(scala.util.Random.nextInt(seq.size))
  }

  private var jEnum: JAgeGroup       = _
  private var ageGroupEnum: AgeGroup = _
  private var vanillaEnum: AgeGroupVanilla.AgeGroupVanilla = _

  @Setup(Level.Trial)
  def setup(): Unit = {
    jEnum = randomFrom(JAgeGroup.values())
    ageGroupEnum = randomFrom(AgeGroup.values)
    vanillaEnum = randomFrom(AgeGroupVanilla.values.toSeq)
  }

  @Benchmark
  def jEnumEnumMapGet(bh: Blackhole): Unit = bh.consume {
    jEnumEnumMap.get(jEnum)
  }

  @Benchmark
  def jEnumScalaMapGet(bh: Blackhole): Unit = bh.consume {
    jEnumScalaMap.get(jEnum)
  }

  @Benchmark
  def enumeratumScalaMapGet(bh: Blackhole): Unit = bh.consume {
    ageGroupScalaMap.get(ageGroupEnum)
  }

  @Benchmark
  def vanillaScalaMapGet(bh: Blackhole): Unit = bh.consume {
    vanillaScalaMap.get(vanillaEnum)
  }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM