scala flatMap или фильтр, который дешевле - PullRequest
0 голосов
/ 29 января 2019

Я хочу использовать поток с ключами, мне было интересно, какой подход лучше с высокой пропускной способностью. Допустим, у меня есть следующее

trait A{
  val id: Int 
  def isFoo: Boolean
}
case class Foo(id: Int) extends A{
  override def isFoo = true
}
case class Bar(id: Int) extends A{
  override def isFoo = false
}
val as = List[A](Foo, Bar)
val fs: List[Foo] = as.flatMap{
  case Foo => Some(Foo)
  case _ => None
}

У меня может быть следующий поток

val src: DataStream[A] = env.fromElements(as:_*)

У меня есть эти варианты:

  1. src.filter(_.isFoo).keyBy(_.id).map(...more processing...)
  2. src.filter(_.isInstanceOf[Foo]).keyBy(_.id).map(...more processing...)//here we can remove the isFoo method
  3. src.flatMap{ case f:Foo => Some(f) case _ => None }.keyBy(_.id).map(...more processing...) Единственная причина, по которой у меня есть опция 3 , будетпозвольте мне удалить поле id из Bar и из самой черты, потому что это создаст поток Foo, который является более прямым.Тем не менее, фильтрация по полю кажется гораздо более легким вариантом, чем отображение , особенно с высокой пропускной способностью.Как вы думаете ?

1 Ответ

0 голосов
/ 29 января 2019

Краткий ответ: это почти наверняка не имеет значения, и вы теряете время, чтобы беспокоиться о том, какой из этих конкретных способов написания операции фильтрации является самым быстрым.Часть ...more processing... почти наверняка станет узким местом в вашем приложении, и вам следует написать версию фильтра, которую вы найдете наиболее понятной и простой в обслуживании.

Немного более длинный ответ: даже если это было как-то странноВ случае, когда один из этих вариантов был заметно лучше, чем другие, что фактически влияло на производительность вашего приложения, вам гораздо лучше написать свой собственный сравнительный анализ для вашего конкретного случая, чем просить незнакомцев на Stack Overflow рассуждать о вашем использовании.case.

Если вы действительно, действительно хотите, чтобы незнакомец в Stack Overflow рассуждал о вашем сценарии использования, я бы сказал, что flatMap в Option, вероятно, не лучший выбор., поскольку это приводит к большому количеству ненужных выделений.Вот сверхбыстрый тест для некоторых альтернативных реализаций (использование Vector вместо Flink, поэтому возьмите результаты с небольшим количеством соли):

import org.openjdk.jmh.annotations._

@State(Scope.Thread) class FilterBenchmark {
  val values: Vector[A] = (0 to 100).map {
    case i if i % 2 == 0 => Foo(i)
    case i => Bar(i)
  }.toVector

  @Benchmark def withIsFoo: Vector[A] = values.filter(_.isFoo)

  @Benchmark def withIsInstanceOf: Vector[A] = values.filter(_.isInstanceOf[Foo])

  @Benchmark def withFlatMap: Vector[A] = values.flatMap {
    case f @ Foo(_) => Some(f)
    case _ => None
  }

  @Benchmark def withFlatMapTypeMatch: Vector[A] = values.flatMap {
    case f: Foo => Some(f)
    case _ => None
  }

  @Benchmark def withCollect: Vector[A] = values.collect {
    case f @ Foo(_) => f
  }

  @Benchmark def withCollectTypeMatch: Vector[A] = values.collect {
    case f: Foo => f
  }
}

И некоторые результаты (2.12.8):

Benchmark                              Mode  Cnt        Score      Error  Units
FilterBenchmark.withCollect           thrpt   10  1359035.689 ± 2749.815  ops/s
FilterBenchmark.withCollectTypeMatch  thrpt   10  1361227.743 ± 2337.850  ops/s
FilterBenchmark.withFlatMap           thrpt   10   113074.826 ±  288.107  ops/s
FilterBenchmark.withFlatMapTypeMatch  thrpt   10   113188.419 ±  262.826  ops/s
FilterBenchmark.withIsFoo             thrpt   10  1254404.326 ± 3997.759  ops/s
FilterBenchmark.withIsInstanceOf      thrpt   10  1257725.670 ± 6115.773  ops/s

Мораль этой истории (на мой взгляд) состоит в том, что flatMap отклонен на порядок и явно плох, но не существует достаточного различия между другими вариантами, чтобы сделать производительность релевантной длярешение.

...