Scala并行排序

Scala并行排序,scala,collections,parallel-processing,Scala,Collections,Parallel Processing,如何按升序排列ParArray集合,例如 ParArray(1,3,2) 或者,哪些平行集合可能更适合此目的 更新 如何在ParArray上实现并行算法,这可能比强制转换到非并行集合进行顺序排序更有效?Scala标准库中没有可用的并行排序算法。因此,并行集合不提供排序方法、排序方法或排序方法。排序之前,您必须转换为适当的顺序类(例如,使用toArray)。如果数据可以放入内存,则单线程内存排序足够快。如果需要从磁盘或HDF加载大量数据,那么可以在hadoop或spark等分布式系统上进行排序

如何按升序排列
ParArray
集合,例如

ParArray(1,3,2)
或者,哪些平行集合可能更适合此目的

更新


如何在
ParArray
上实现并行算法,这可能比强制转换到非并行集合进行顺序排序更有效?

Scala标准库中没有可用的并行排序算法。因此,并行集合不提供排序方法、排序方法或排序方法。排序之前,您必须转换为适当的顺序类(例如,使用
toArray
)。

如果数据可以放入内存,则单线程内存排序足够快。如果需要从磁盘或HDF加载大量数据,那么可以在hadoop或spark等分布式系统上进行排序

如何在ParArray上实现并行算法,这可能会证明 比强制转换到非并行集合以实现连续 分类

我的第一个发现是,将并行阵列“转换”为顺序阵列并返回,似乎不会有太多性能损失:

def time[R](block: => R): R = {
  val t0 = System.nanoTime()
  val result = block    // call-by-name
  val t1 = System.nanoTime()
  val diff: Long = t1 - t0
  println(s"Elapsed time: ${diff * 1.0 / 1E9}s")
  result
}

def main(args: Array[String]): Unit = {
  val size: Int = args.headOption.map(_.toInt).getOrElse(1000000)
  val input = Array.fill(size)(Random.nextInt())
  val arrayCopy: Array[Int] = Array.ofDim(size)
  input.copyToArray(arrayCopy)
  time { input.sorted }
  val parArray = arrayCopy.par
  val result = time { parArray.seq.sorted.toArray.par }
}
给予

> run 1000000
[info] Running Runner 1000000
Elapsed time: 0.344659236s
Elapsed time: 0.321363896s
对于我测试的所有
数组
大小,结果都非常相似,并且通常以某种方式支持第二个表达式。因此,如果您担心转换为顺序收集并返回会扼杀您在其他操作中获得的性能提升,我认为您不应该这样做

当涉及到利用Scala的并行集合来实现并行排序时,在某些情况下,并行排序的性能会比默认值更好——我不认为有明显的好方法可以做到这一点,但尝试一下也无妨:

我认为应该做的是将输入数组拆分成与计算机内核数量相同的子数组(最好不进行任何不必要的复制),并同时对部分进行排序。之后,人们可能会把这些部分合并在一起。下面是代码的外观:

val maxThreads = 8 //for simplicity we're not configuring the thread pool explicitly
val groupSize:Int = size/maxThreads + 1
val ranges: IndexedSeq[(Int, Int)] = (0 until maxThreads).map(i => (i * groupSize, (i + 1) * groupSize))
time {
  //parallelizing sorting for each range
  ranges.par.foreach {case (from, to) =>
    input.view(from, to).sortWith(_ < _)
  }
  //TODO merge the parts together
}
val maxThreads=8//为了简单起见,我们没有显式地配置线程池
val groupSize:Int=size/maxThreads+1
val范围:IndexedSeq[(Int,Int)]=(0到maxThreads)。映射(i=>(i*groupSize,(i+1)*groupSize))
时间{
//为每个范围并行排序
Real.PAR.FACH{{(从,to)= >
input.view(from,to).sortWith(\u<\ u
}
//TODO将零件合并在一起
}
不幸的是,有一点阻碍了我们对视图进行任何有趣的操作。似乎没有任何Scala内置机制(视图除外)来对集合的一部分进行排序。这就是为什么我试着用def
mergeSort(a:Array[Int],r:Range):Unit
的签名来编写我自己的合并排序算法,以便像我上面描述的那样使用它。不幸的是,它的效率似乎比scala
Array.sorted
方法低4倍多,因此我不认为它可以用来提高标准顺序方法的效率


如果我正确理解您的情况,那么您的数据集适合内存,因此使用Hadoop和MapReduce之类的工具还为时过早。您可以尝试的是,除了添加依赖项之外,您不需要设置任何群集或安装任何东西,Spark就可以在基本配置中使用机器的所有核心。它的RDD在思想上类似于Scala的并行集合,但具有额外的功能。它们()支持并行排序

如果您根据Java 8构建Scala项目,您可以使用以下方法:

def sort[T <: Comparable](parArray: ParArray[T])(implicit c: ClassTag[T]): ParArray[T] = {
   var array = new Array[T](parArray.size) // Or, to prevent copying, var array = parArray.seq.array.asInstanceOf[Array[T]] might work?
   parArray.copyToArray(array)
   java.util.Arrays.parallelSort(array)
   ParArray.createFromCopy(array)
}

def排序[T感谢您的回答,请注意此问题的更新。这是一个很好的观察结果,但对于依赖项而言,它更倾向于采用轻量级方法;理想情况下是Scala本身的实现。我想您最好的选择是使用合并排序算法。您可以尝试使用Hadoop和MapReduce实现它。的答案应提供您正在寻找的答案。我喜欢这个函数式答案,但我想您希望使用树而不是
TreeSet
,因为集合可以消除重复项。
def parallelSort[A : Ordering](seq: ParIterable[A]): TreeSet[A] = {
  seq.aggregate[TreeSet[A]](TreeSet.empty[A])(
  (set, a) => set + a,
  (set, set) => set ++ set)
}