简体   繁体   中英

Scala quickSort 10x slower when using Ordering[T]

I was doing some sorting of integer indices based on a custom Ordering. I found that the Ordering[T] used here makes the sort at least 10 times slower than handcrafted quickSort using direct calls to the compare method. That seems outrageously costly!

val indices: Array[Int] = ...

class OrderingByScore extends Ordering[Int] { ... }

time { (0 to 10000).par.foreach(x => {
  scala.util.Sorting.quickSort[Int](indices.take(nb))(new OrderingByScore)
})}
// Elapsed: 30 seconds

Compared to the hand crafted sortArray found here but modified to add an ord: Ordering[Int] parameter:

def sortArray1(array: Array[Int], left: Int, right: Int, ord: Ordering[Int]) = ...

time { (0 to 10000).par.foreach(x => {
  sortArray1(indices.take(nb), 0, nb - 1, new OrderingByScore)
})}
// Elapsed: 19 seconds

And finally, same piece of code but using exact type instead ( ord: OrderingByScore ):

def sortArray2(array: Array[Int], left: Int, right: Int, ord: OrderingByScore) = ...

time { (0 to 10000).par.foreach(x => {
  sortArray2(indices.take(nb), 0, nb - 1, new OrderingByScore)
})}
// Elapsed: 1.85 seconds

I'm quite surprised to see such a difference between each versions!

In my example, indices array is sorted based on the values found in another Doubles array containing a combined scores. Also, the sorting is stable as it use the indices itself as a secondary comparison. On a side note, to make testing reliable, I had to "indices.take(nb)" within the parallel loop since sorting modifies input array. This penalty in negligible compared to the problem that brings me here. Full code on gist here .

Your suggestions are much welcomed to improve on.. But try not to change the basic structure of an indices and scores arrays.

Note: I'm running within scala 2.10 REPL.

The problem is that scala.math.Ordering is not specialized. So every time you call the compare method with a primitive like Int , both arguments of type Int are being boxed to java.lang.Integer . That is producing a lot of short-lived objects, which slows things down considerably.

The spire library has a specialized version of Ordering called spire.algebra.Order that should be much faster. You could just try to substitute it in your code and run your benchmark again.

There are also sorting algorithms in spire. So maybe just try those.

Basically, whenever you want to do math with primitives in a high performance way, spire is the way to go.

Also, please use a proper microbenchmarking tool like Thyme or JMH for benchmarks if you want to trust the results.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM