2010-03-13 11 views
12

Un collega aveva bisogno di ordinare una matrice di oggetti ActiveRecord in un'app Rails. Ha provato l'ovvio Array.sort! ma sembrava sorprendentemente lento, prendendo 32 secondi per un array di 3700 oggetti. Quindi, nel caso in cui questi grandi oggetti grassi rallentassero le cose, ha reimplementato l'ordinamento ordinando una serie di piccoli oggetti, quindi riordinando la matrice originale di oggetti ActiveRecord in modo che corrispondessero - come mostrato nel codice qui sotto. Tada! L'ordinamento ora richiede 700ms.Ruby: Perché Array.sort è lento per oggetti di grandi dimensioni?

Questo mi ha davvero sorpreso. Il metodo di ordinamento di Ruby finisce per copiare oggetti sul luogo anziché solo riferimenti? Sta usando 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 
+1

vostro '<=> metodo' può anche essere scritta come: (@time <=> b.time) .nonzero '? o @sqn <=> b.sqn' –

+2

Il registro di registrazione attivo mostra qualcosa di interessante durante l'ordinamento? Assicurati che sia configurato per registrare le query del database. –

+0

Glenn - Grazie per il suggerimento su <=>. Wayne - Penso che potresti avere la risposta. Dopo non aver ottenuto alcuna risposta definitiva qui su SO ho preso in giro un piccolo script di test per ordinare alcuni oggetti ActiveRecord di grandi dimensioni (riempiti con alcune stringhe casuali) e quindi ho ripetuto l'ordinamento utilizzando la tecnica sopra riportata. Nessun miglioramento. Quindi lunedì suggerirò al mio collega di cercare gli effetti collaterali durante l'ordinamento. –

risposta

6

sort sicuramente non copia gli oggetti. Una differenza che posso immaginare tra il codice che utilizza EventSorter e il codice senza (che non hai fornito, quindi devo indovinare) è che EventSorter chiama event.sqn e event.time esattamente una volta e memorizza il risultato in variabili. Durante l'ordinamento è necessario accedere solo alle variabili. La versione originale presumibilmente si chiamava sqn e time ogni volta che veniva richiamato il blocco di ordinamento.

In questo caso, può essere risolto utilizzando sort_by anziché ordinamento. sort_by chiama il blocco solo una volta per oggetto e quindi utilizza i risultati memorizzati nella cache del blocco per ulteriori confronti.

+0

Hai indovinato: l'Event ha un metodo quasi identico <=> su EventSorter, ma nel caso di Event, sqn e time sono i nomi delle colonne nel database. Ciò significa che Rails/ActiveRecord fornisce metodi sqn e time, che sembra analizzare i valori nell'hash degli attributi ActiveRecord ogni volta che vengono chiamati. Quindi ogni volta Evento. <=> è stato chiamato ActiveRecord stava analizzando una stringa di tempo in un oggetto Ruby Time, quindi le prestazioni orribile. Mistero risolto! Grazie. –

0

Nulla risponde a domande come questa meglio del codice sorgente della lingua corrente. Array # sorta! usa sort_internal() che è definito in array.c:

sort_internal()

(Sì, lo so che è le fonti per 1.8.4, ma non riesco a trovare 1.8.6 quelli on-line e sono abbastanza sicuro che questo non è cambiato)

+1

Continua - dammi un indizio! Non sono abbastanza fluente in C per fare molto di questo. –

+0

Oh, mi dispiace per quello! Fondamentalmente utilizza l'ordinamento rapido, che è compreso tra O (N^2) (worst case) e O (N log N) (caso migliore). –

+3

Ma questo non sembra spiegare perché è più lento ordinare una matrice di oggetti di grandi dimensioni piuttosto che una serie di piccoli oggetti.L'implementazione richiede di copiare gli oggetti attorno all'heap piuttosto che semplicemente riorganizzare i puntatori? –

2

Proprio come una spiegazione di ciò che è probabile che accada e come trattare con esso ...

Ordinamento tende a guardare un elemento più volte in modo una ricerca costosa in oggetto o struttura diventerà molto costoso molto rapidamente .

Una trasformazione di Schwartz è comunemente utilizzata per ordinare array di oggetti o strutture complessi. L'idea di base è precalcolare un valore semplice che rifletta accuratamente la grande struttura o l'oggetto, quindi ordinare i valori, quindi utilizzare la matrice ordinata risultante per fare riferimento alla cosa da cui provengono.

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

Problemi correlati