【问题标题】:Fastest way to take elementwise sum of two Lists对两个列表进行元素总和的最快方法
【发布时间】:2020-03-26 07:09:09
【问题描述】:

我可以使用 Zipped 函数进行元素运算,例如求和。让我有两个列表 L1L2 如下所示

val L1 = List(1,2,3,4)
val L2 = List(5,6,7,8)

我可以通过以下方式逐元素求和

(L1,L2).zipped.map(_+_)

结果是

List(6, 8, 10, 12) 

正如预期的那样。

我在我的实际代码中使用 Zipped 函数,但它需要太多时间。实际上,我的列表大小超过 1000,并且我有超过 1000 个列表,而且我的算法是迭代的,其中迭代可能高达 10 亿 .

在代码中我必须做以下事情

list =( (L1,L2).zipped.map(_+_).map (_  * math.random) , L3).zipped.map(_+_)

L1L2L3的尺寸相同。此外,我必须在集群上执行我的实际代码。

在 Scala 中获取列表元素和的最快方法是什么?

【问题讨论】:

  • 如果您正在寻找运行时效率,请考虑使用数组而不是列表。
  • @AndriyPlokhotnyuk 我尝试用数组更改列表,但我认为这不会提高速度。
  • 为了提高效率,使用预先分配的数组来存储临时结果,并使用 while 循环和按索引访问对它们进行操作。
  • 此外,我建议不要发明轮子,而是使用一些高效的线性代数库,例如:github.com/scalanlp/breeze
  • 如果您对性能不满意,请尝试基于 SIMD 的解决方案,例如:astojanov.github.io/blog/2017/12/20/scala-simd.html

标签: scala list performance apache-spark elementwise-operations


【解决方案1】:

一种选择是使用 Streaming 实现,利用 惰性 可能会提高性能。

使用 LazyList 的示例 (在 Scala 中引入 2.13

def usingLazyList(l1: LazyList[Double], l2: LazyList[Double], l3: LazyList[Double]): LazyList[Double] =
  ((l1 zip l2) zip l3).map {
    case ((a, b), c) =>
      ((a + b) * math.random()) + c
  }

还有一个使用fs2.Stream的例子(由fs2库引入)

import fs2.Stream
import cats.effect.IO

def usingFs2Stream(s1: Stream[IO, Double], s2: Stream[IO, Double], s3: Stream[IO, Double]): Stream[IO, Double] =
  s1.zipWith(s2) {
    case (a, b) =>
      (a + b) * math.random()
  }.zipWith(s3) {
    case (acc, c) =>
      acc + c
  }

但是,如果这些仍然太慢,最好的选择是使用普通数组。

这是一个使用 ArraySeq (在 Scala 2.13 中也引入)的示例,它至少会保持不变性。如果您愿意,可以使用原始数组,但要小心。
(如果需要,您也可以使用 collections-parallel module 以提高性能)

import scala.collection.immutable.ArraySeq
import scala.collection.parallel.CollectionConverters._

def usingArraySeq(a1: ArraySeq[Double], a2: ArraySeq[Double], a3: ArraySeq[Double]): ArraySeq[Double] = {
  val length = a1.length

  val arr = Array.ofDim[Double](length)

  (0 until length).par.foreach { i =>
    arr(i) = ((a1(i) + a2(i)) * math.random()) + a3(i)
  }

  ArraySeq.unsafeWrapArray(arr)
}

【讨论】:

  • Zipped 比 Zip 快。
  • LazyList 示例中的 @user12140540?我对此表示怀疑。 zipped 相对于zip 的优势在于前者是懒惰的,而后者是严格的。但是,由于 LazyList 已经是惰性的,因此两者的行为应该相同。我认为您的代码的问题在于每个 map 调用实际上是严格的,因此您将每个列表迭代了几次,这就是为什么我建议使用完整的 lazy 集合来保证最后只有一次遍历。或者,如果这还不足以满足您所需的性能,那么没有什么可以超越普通的 Array 操作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-03-29
  • 2020-11-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-14
  • 2021-02-24
相关资源
最近更新 更多