Scala реализация карты перечисления - PullRequest
2 голосов
/ 24 февраля 2020

Java имеет EnumMap - специальная реализация Map, предназначенная для случая, когда ключи имеют тип enum (см. Объявление класса EnumMap<K extends Enum<K>,V>). Основным преимуществом, например, над java.util.HashMap является эффективность использования памяти и производительности, поскольку он заранее знает количество ключей, поскольку они объявлены в перечислении, поэтому он компактен и быстрее, поскольку нет необходимости расширять внутреннюю таблицу. и разрешить конфликты sh.

Мне интересно, есть ли какая-либо реализация Scala из коробки для этой реализации Java, но с Scala s Enumeration и immutable.Map интерфейс. Может быть, какая-либо внешняя реализация lib или какой-либо способ создания собственной реализации с близкими памятью и результатами производительности?

Большое спасибо за помощь заранее!

1 Ответ

1 голос
/ 26 февраля 2020

Открытый выпуск 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 примерно в 2 раза быстрее, чем обычные Scala Set и Maps с Enumeratum, но, учитывая, что мы все еще в диапазоне менее 10 нс, кажется разумным сказать, что производительность здесь не будет узкого места, за исключением случаев с супер-гранями.

Я попытался повторить тесты Ллойдметы и получил следующие результаты с Scala 2.13, перечисление 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, кажется, поймали с enumeratumScalaMapGet с 2017 года и тривиальным выделением памяти 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)
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...