Michael Pohl
07/12/2021, 6:29 PMQuickSort
implementation takes vs. Kotlin's own List<T>.sorted()
.
While I was on it, I also tested variations of my implementation as an extension function and without explicitly storing values in the function. And I noticed they are very different in how fast they are processed. What is causing this? See thread ->Michael Pohl
07/12/2021, 6:31 PMmyList.sorted()
2. My basic implementation
fun quickSort(input: List<Int>): List<Int> {
if (input.size < 2) return input
val pivot = input[input.size / 2]
val smaller = input.filter { it < pivot }
val equal = input.filter { it == pivot }
val bigger = input.filter { it > pivot }
return quickSort(smaller) + equal + quickSort(bigger)
}
3. The same as an extension
fun List<Int>.quickSortExtension(): List<Int> {
if (this.size < 2) return this
val pivot = this[this.size / 2]
val smaller = this.filter { it < pivot }
val equal = this.filter { it == pivot }
val bigger = this.filter { it > pivot }
return smaller.quickSortExtension() + equal + bigger.quickSortExtension()
}
4. Without explicitly storing the vals
fun quickSort2(input: List<Int>): List<Int> {
if (input.size < 2) return input
val pivot = input[input.size / 2]
return quickSort2(
input.filter { it < pivot }) +
input.filter { it == pivot } +
quickSort2(input.filter { it > pivot })
}
Now what I found curious was that they take extremely differnet times amounts to complete. I measured them in a very simple way:
val result = measureNanoTime {
[myQuickSortFunction(myRandomArray)]
}/1000 // returns microseconds
So for an array with 1000 elements the average times in microseconds are like this:
1. Kotlin: 1800
2. No-val QuickSort: 3400
3. Basic QuickSort: 3600
4. Extension QuickSort: 13000
While the difference between no.2 and 3 might be negligible, it is still there - where does it come from? And how can it be explained that the extension function is so much slower - it takes more than 3 times as long? And what does the stdlib .sorted()
do different?
Now to make this even more interesting (to me at least 🙂 ), when testing the same methods with an array that just contains 10 items, the results are very different:
1. Basic QuickSort: 85
2. No-val QuickSort: 95
3. Kotlin: 650
4. Extension QuickSort: 6500
What causes these differences? Especially, why is Kotlin's standard sorting so much slower for small arrays than it is for larger ones? And what causes the extension function to be so much slower? Does that mean there is a general performance impact when using extensions vs. "regular" functions?andylamax
07/12/2021, 9:04 PMNir
07/13/2021, 12:09 AMNir
07/13/2021, 12:09 AMNir
07/13/2021, 12:11 AMNir
07/13/2021, 12:11 AMNir
07/13/2021, 12:15 AMNir
07/13/2021, 12:15 AMpniederw
07/13/2021, 12:24 AMfilter()
and +
creates a new list. Another difference is that Kotlin's sorted()
uses Java's Timsort, which can be significantly faster than QuickSort. To compare the performance of sorting algorithms, consider sorting arrays (see Kotlin's Array.sort()
). Always use JMH for JVM microbenchmarks.Dave K
07/13/2021, 1:32 AMMichael Pohl
07/13/2021, 5:03 AMpniederw
07/13/2021, 5:26 AMephemient
07/13/2021, 5:43 AMephemient
07/13/2021, 5:45 AM.sorted()
copies to an Array, which can be accessed via bytecode insructions rather than List's `get()`/`set()`, uses Java's built-in sort on that (which is likely timsort under the covers), and copies back to a Listephemient
07/13/2021, 5:46 AMephemient
07/13/2021, 5:53 AMephemient
07/13/2021, 5:55 AMephemient
07/13/2021, 5:57 AMPaul Griffith
07/13/2021, 8:45 PMPaul Griffith
07/13/2021, 8:45 PMMyBenchmark.quickSort2 thrpt 25 2787.005 ± 66.497 ops/s
MyBenchmark.quickSortExtension thrpt 25 2848.910 ± 11.635 ops/s
MyBenchmark.quickSortNonExtension thrpt 25 3112.069 ± 45.936 ops/s
MyBenchmark.sortBoxedArrayInPlace thrpt 25 511593.816 ± 12580.323 ops/s
MyBenchmark.sortBoxedArrayToArray thrpt 25 11702.525 ± 112.049 ops/s
MyBenchmark.sortBoxedArrayToList thrpt 25 12085.770 ± 360.113 ops/s
MyBenchmark.sortListInPlace thrpt 25 1140047.824 ± 13258.572 ops/s
MyBenchmark.sortListToList thrpt 25 12124.497 ± 165.510 ops/s
MyBenchmark.sortPrimitiveArrayInPlace thrpt 25 4523362.961 ± 176819.205 ops/s
MyBenchmark.sortPrimitiveArrayToArray thrpt 25 86727.839 ± 1426.855 ops/s
MyBenchmark.sortPrimitiveArrayToList thrpt 25 10678.672 ± 120.942 ops/s
ephemient
07/13/2021, 8:59 PMephemient
07/13/2021, 9:00 PMPaul Griffith
07/13/2021, 9:02 PMephemient
07/13/2021, 9:04 PMPaul Griffith
07/13/2021, 11:11 PM