对两个列表进行元素求和的最快方法

Asi*_*sif 7 performance scala list apache-spark elementwise-operations

我可以使用Zipped函数进行像 sum 这样的元素操作。让我有两个列表L1L2,如下所示

val L1 = List(1,2,3,4)
val L2 = List(5,6,7,8)
Run Code Online (Sandbox Code Playgroud)

我可以按以下方式计算元素总和

(L1,L2).zipped.map(_+_)
Run Code Online (Sandbox Code Playgroud)

结果是

List(6, 8, 10, 12) 
Run Code Online (Sandbox Code Playgroud)

正如预期的那样。

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

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

list =( (L1,L2).zipped.map(_+_).map (_  * math.random) , L3).zipped.map(_+_)
Run Code Online (Sandbox Code Playgroud)

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

在 Scala 中对 List 进行元素求和的最快方法是什么?

Lui*_*rez 3

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

使用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
  }
Run Code Online (Sandbox Code Playgroud)

以及使用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
  }
Run Code Online (Sandbox Code Playgroud)

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

下面是一个使用ArraySeq 2.13也在 Scala 中引入)的示例,它至少会保留不变性。如果您愿意,可以使用原始数组,但要小心。
(如果您愿意,您也可以使用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)
}
Run Code Online (Sandbox Code Playgroud)