Scala Коллекция отсортирована, sortWith и sortBy Производительность

Scala включает несколько методов в стандартную библиотеку для сортировки списка, например для сортировки списка списка, можно использовать:

list.sorted
list.sortWith(_<_)
list.sortBy(x=>x)

Хотя это могут быть самые простые способы сортировки списка, я обнаружил, что для больших списков они имеют существенный недостаток производительности.

Например, чтобы отсортировать миллион целых чисел, сортировка занимает в среднем 500 мс, а sortWith и sortBy - около 700 мс. Это сравнивается с scala.util.Sorting.quickSort, который занимает около 120 мс и java.util.Arrays.sort, который занимает около 100 мс. Для более крупных списков эта множественная разность факторов наблюдается по мере дальнейшего увеличения. Шаблон показан на следующей диаграмме.

Performance of various Scala sorting methods

В чем причина этого отставания в производительности? И почему не используются более эффективные алгоритмы/реализации, используемые для стандартных методов?

Ответы

Ответ 1

Обратите внимание, что линии имеют одинаковый наклон, но смещены друг от друга? С логарифмической шкалой мы смотрим на постоянную разницу факторов. sorted и друзья оплачивают стоимость преобразования List в Array, сортировка (с помощью java.util.Arrays.sort, фактически) и преобразование обратно в List. scala.util.Sorting.quickSort и java.util.Arrays.sort работают непосредственно с массивами. Фактор log n в производительности quicksort n log n в значительной степени не имеет значения, поэтому с линейным временем, необходимым для создания массива и итогового списка, мы получаем постоянную разницу коэффициентов. Помните, что List имеет ячейку cons для каждого элемента, что делает массивный объем произвольного доступа при создании Array, а затем для создания нового List требуется время, затрачиваемое на выделение памяти, и, по всей вероятности, цикл сбора мусора или два.

Для списков примитивов это еще хуже. List является общим, поэтому любые примитивы должны быть помещены в бокс, что добавляет еще один слой косвенности. И, к сожалению, созданный Array также имеет значения в коробке. Фактически, вы заканчиваете сортировку Array[java.lang.Integer], когда вы действительно хотите сортировать Array[Int].

Подводя итог: алгоритмы сортировки идентичны, но есть веские причины, по которым изменяемые массивы превосходят неизменные одиночные списки.