Ruby: Почему Array.sort является медленным для больших объектов?

Коллега должен был отсортировать массив объектов ActiveRecord в приложении для направляющих. Он попробовал очевидное Array.sort! но это казалось удивительно медленным, беря 32 для массива 3 700 объектов. Так на всякий случай это были эти большие объекты, замедляющие вещи, он повторно реализовал вид путем сортировки массива маленьких объектов, затем переупорядочение исходного массива ActiveRecord возражает против соответствия - как показано в коде ниже. Tada! Вид теперь берет 700 мс.

Это действительно удивило меня. Метод сортировки Ruby заканчивает тем, что копировал объекты о месте, а не просто ссылках? Он использует Ruby 1.8.6/7.

def self.sort_events(events)
  event_sorters = Array.new(events.length) {|i| EventSorter.new(i, events[i])}
  event_sorters.sort!
  event_sorters.collect {|es| events[es.index]} 
end

private

# Class used by sort_events
class EventSorter
  attr_reader :sqn
  attr_reader :time
  attr_reader :index

  def initialize(index, event)
    @index = index  
    @sqn   = event.sqn
    @time  = event.time  
  end

  def <=>(b)
    @time != b.time ? @time <=> b.time : @sqn <=> b.sqn
  end
end
12
задан David Waller 6 July 2011 в 13:58
поделиться

3 ответа

sort определенно не копирует объекты. Одно различие, которое я могу представить между кодом, использующим EventSorter, и кодом без него (который вы не предоставили, поэтому я должен догадаться) заключается в том, что EventSorter вызывает event.sqn и event.time ровно один раз и сохраняет результат в переменных. Во время сортировки нужно иметь доступ только к переменным. Исходная версия предположительно вызывала sqn и time каждый раз при вызове блока сортировки.

Если это так, это можно исправить, используя sort_by вместо sort. sort_by вызывает блок только один раз для каждого объекта, а затем использует кешированные результаты блока для дальнейших сравнений.

6
ответ дан 2 December 2019 в 22:51
поделиться

Ничто не отвечает на подобные вопросы лучше, чем исходный код реального языка. Массив # sort! использует sort_internal (), который определен в array.c:

sort_internal ()

(Да, я знаю, что это источники для 1.8.4, но я не могу найти источники 1.8.6 в Интернете, и я почти уверен, что это не изменилось.)

0
ответ дан 2 December 2019 в 22:51
поделиться

Просто в качестве объяснения того, что, вероятно, происходит и как с этим бороться...

Сортировка имеет тенденцию просматривать элемент несколько раз, поэтому дорогой поиск в объекте или структуре быстро станет очень дорогим.

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

http://en.wikipedia.org/wiki/Schwartzian_transform

2
ответ дан 2 December 2019 в 22:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: