Scala中的数组排序:性能问题 val a=新数组[(Int,Int)](250000000) ... // 这里初始化 // #1 val b=a.sortBy(u.u 1) //这部分完全杀死了GC, //我还分配了大量堆内存(30GB) //比它想用的还要多 // #2 val b=a //与#1一样有效地杀死GC // #3 val b=a.sortWith(u.\u 1:A](隐式命令:排序[B]):Repr={ val len=此长度 val b=新造船商 如果(len==1)b++=this 否则如果(len>1){ b、 sizeHint(len) val arr=new Array[AnyRef](len)//以前使用的ArraySeq用于更紧凑但速度较慢的代码 变量i=0 对于(x)你在对零数组进行排序吗?不,是随机整数。原语总是比对象更快,占用的内存更少。在你的快速示例中,你使用原语长值而不是元组对象,在所有情况下使用长值或在所有情况下使用元组都更公平,但有时不是一个,有时是另一个它也是唯一一个不创建数组副本的数组(arrays.sort sorts in place而不是创建排序副本)。@puhlen我知道原语更快、更紧凑(仍然是5x,看起来很糟糕,Long=8字节,(Int,Int)=8字节,没有开销)。但是#1和#2生成大量垃圾的事实令人不安(可以轻松达到20GB的标记,而对于#3则可以达到稳定的10GB)。@VictorMoroz(Int,Int)至少是20字节,而且可以非常好,java中的对象有很多开销。看看你的测试是如何使用元组代替长时间运行的。你还必须考虑复制这样一个大数组的影响。大数组不仅需要大量内存,而且内存必须是连续的,这可能需要GC循环。压缩堆以获得足够大的空间。sortWith也在使用sorted,但正如我所提到的,它比数组慢。sort不使用比较器,但可以工作。sortBy和sorted根本不需要。#1需要在比较之前对数组的每个元素应用一个函数-对于每个比较,rath呃不止一次,#2需要比较整个对,所以有更多的比较,#3只是做比较。不仅仅是时间,是途中产生的垃圾量,这会影响时间和CPU负载(通过sortBy创建一个整数数组并不能解释它,远远不止这些)。只是没有从库函数中得到它。 def sorted[B >: A](implicit ord: Ordering[B]): Repr = { val len = this.length val b = newBuilder if (len == 1) b ++= this else if (len > 1) { b.sizeHint(len) val arr = new Array[AnyRef](len) // Previously used ArraySeq for more compact but slower code var i = 0 for (x <- this) { arr(i) = x.asInstanceOf[AnyRef] i += 1 } java.util.Arrays.sort(arr, ord.asInstanceOf[Ordering[Object]]) i = 0 while (i < arr.length) { b += arr(i).asInstanceOf[A] i += 1 } } b.result() }

Scala中的数组排序:性能问题 val a=新数组[(Int,Int)](250000000) ... // 这里初始化 // #1 val b=a.sortBy(u.u 1) //这部分完全杀死了GC, //我还分配了大量堆内存(30GB) //比它想用的还要多 // #2 val b=a //与#1一样有效地杀死GC // #3 val b=a.sortWith(u.\u 1:A](隐式命令:排序[B]):Repr={ val len=此长度 val b=新造船商 如果(len==1)b++=this 否则如果(len>1){ b、 sizeHint(len) val arr=new Array[AnyRef](len)//以前使用的ArraySeq用于更紧凑但速度较慢的代码 变量i=0 对于(x)你在对零数组进行排序吗?不,是随机整数。原语总是比对象更快,占用的内存更少。在你的快速示例中,你使用原语长值而不是元组对象,在所有情况下使用长值或在所有情况下使用元组都更公平,但有时不是一个,有时是另一个它也是唯一一个不创建数组副本的数组(arrays.sort sorts in place而不是创建排序副本)。@puhlen我知道原语更快、更紧凑(仍然是5x,看起来很糟糕,Long=8字节,(Int,Int)=8字节,没有开销)。但是#1和#2生成大量垃圾的事实令人不安(可以轻松达到20GB的标记,而对于#3则可以达到稳定的10GB)。@VictorMoroz(Int,Int)至少是20字节,而且可以非常好,java中的对象有很多开销。看看你的测试是如何使用元组代替长时间运行的。你还必须考虑复制这样一个大数组的影响。大数组不仅需要大量内存,而且内存必须是连续的,这可能需要GC循环。压缩堆以获得足够大的空间。sortWith也在使用sorted,但正如我所提到的,它比数组慢。sort不使用比较器,但可以工作。sortBy和sorted根本不需要。#1需要在比较之前对数组的每个元素应用一个函数-对于每个比较,rath呃不止一次,#2需要比较整个对,所以有更多的比较,#3只是做比较。不仅仅是时间,是途中产生的垃圾量,这会影响时间和CPU负载(通过sortBy创建一个整数数组并不能解释它,远远不止这些)。只是没有从库函数中得到它。 def sorted[B >: A](implicit ord: Ordering[B]): Repr = { val len = this.length val b = newBuilder if (len == 1) b ++= this else if (len > 1) { b.sizeHint(len) val arr = new Array[AnyRef](len) // Previously used ArraySeq for more compact but slower code var i = 0 for (x <- this) { arr(i) = x.asInstanceOf[AnyRef] i += 1 } java.util.Arrays.sort(arr, ord.asInstanceOf[Ordering[Object]]) i = 0 while (i < arr.length) { b += arr(i).asInstanceOf[A] i += 1 } } b.result() },scala,garbage-collection,Scala,Garbage Collection,通过杀死GC,我的意思是使用所有16个内核,它工作得很疯狂(我知道我可以减少GC的#个内核,但这并不能解决问题) 好的,我知道#3(对象,不可变操作)有一个开销,不确定它应该花费5倍的时间和4倍的堆,但我猜这是代价。但我对第一和第二感到困惑。我是否应该避免将隐性订购视为一种瘟疫?那里发生了什么事?我可能做错了 Scala版本:2.12.4这些方法要求它使用映射值创建该数组的副本,然后使用自定义比较器应用排序数组。对整数值进行排序要聪明得多-它可以直接比较数字,不必调用任何方法来比较它们 请查看这

通过杀死GC,我的意思是使用所有16个内核,它工作得很疯狂(我知道我可以减少GC的#个内核,但这并不能解决问题)

好的,我知道#3(对象,不可变操作)有一个开销,不确定它应该花费5倍的时间和4倍的堆,但我猜这是代价。但我对第一和第二感到困惑。我是否应该避免将隐性订购视为一种瘟疫?那里发生了什么事?我可能做错了


Scala版本:2.12.4这些方法要求它使用映射值创建该数组的副本,然后使用自定义比较器应用排序<代码>数组。对整数值进行排序要聪明得多-它可以直接比较数字,不必调用任何方法来比较它们

请查看这些方法的实施情况:

val a = new Array[(Int, Int)](250000000)
... // initialization here

// #1
val b = a.sortBy(_._1)
// This part completely kills GC, 
// and I allocate plenty of heap memory (30GB)
// more than it's trying to use

// #2
val b = a.sorted
// Kills GC as efficiently as #1

// #3
val b = a.sortWith(_._1 < _._1)
// This part doesn't kill GC, but
// takes long to complete (224s)
// and still requires 10GB of heap

// #4
val a = new Array[Long](250000000)
java.util.Arrays.sort(a)
// Alternative version
// which takes only 2.7GB
// and completes in 40 secs
def排序[B>:A](隐式命令:排序[B]):Repr={
val len=此长度
val b=新造船商
如果(len==1)b++=this
否则如果(len>1){
b、 sizeHint(len)
val arr=new Array[AnyRef](len)//以前使用的ArraySeq用于更紧凑但速度较慢的代码
变量i=0

对于(x)你在对零数组进行排序吗?不,是随机整数。原语总是比对象更快,占用的内存更少。在你的快速示例中,你使用原语长值而不是元组对象,在所有情况下使用长值或在所有情况下使用元组都更公平,但有时不是一个,有时是另一个它也是唯一一个不创建数组副本的数组(arrays.sort sorts in place而不是创建排序副本)。@puhlen我知道原语更快、更紧凑(仍然是5x,看起来很糟糕,Long=8字节,(Int,Int)=8字节,没有开销)。但是#1和#2生成大量垃圾的事实令人不安(可以轻松达到20GB的标记,而对于#3则可以达到稳定的10GB)。@VictorMoroz
(Int,Int)至少是20字节,而且可以非常好,java中的对象有很多开销。看看你的测试是如何使用元组代替长时间运行的。你还必须考虑复制这样一个大数组的影响。大数组不仅需要大量内存,而且内存必须是连续的,这可能需要GC循环。压缩堆以获得足够大的空间。
sortWith
也在使用
sorted
,但正如我所提到的,它比
数组慢。sort
不使用比较器,但可以工作。
sortBy
sorted
根本不需要。#1需要在比较之前对数组的每个元素应用一个函数-对于每个比较,rath呃不止一次,#2需要比较整个对,所以有更多的比较,#3只是做比较。不仅仅是时间,是途中产生的垃圾量,这会影响时间和CPU负载(通过
sortBy
创建一个整数数组并不能解释它,远远不止这些)。只是没有从库函数中得到它。
def sorted[B >: A](implicit ord: Ordering[B]): Repr = {
  val len = this.length
  val b = newBuilder
  if (len == 1) b ++= this
  else if (len > 1) {
    b.sizeHint(len)
    val arr = new Array[AnyRef](len)  // Previously used ArraySeq for more compact but slower code
    var i = 0
    for (x <- this) {
      arr(i) = x.asInstanceOf[AnyRef]
      i += 1
    }
    java.util.Arrays.sort(arr, ord.asInstanceOf[Ordering[Object]])
    i = 0
    while (i < arr.length) {
      b += arr(i).asInstanceOf[A]
      i += 1
    }
  }
  b.result()
}