Ho implementato un algoritmo di ordinamento di unione naive in Python. Algoritmo e il codice del test è inferiore:Curva di prestazioni inattesa da un tipo di unione CPython
import time
import random
import matplotlib.pyplot as plt
import math
from collections import deque
def sort(unsorted):
if len(unsorted) <= 1:
return unsorted
to_merge = deque(deque([elem]) for elem in unsorted)
while len(to_merge) > 1:
left = to_merge.popleft()
right = to_merge.popleft()
to_merge.append(merge(left, right))
return to_merge.pop()
def merge(left, right):
result = deque()
while left or right:
if left and right:
elem = left.popleft() if left[0] > right[0] else right.popleft()
elif not left and right:
elem = right.popleft()
elif not right and left:
elem = left.popleft()
result.append(elem)
return result
LOOP_COUNT = 100
START_N = 1
END_N = 1000
def test(fun, test_data):
start = time.clock()
for _ in xrange(LOOP_COUNT):
fun(test_data)
return time.clock() - start
def run_test():
timings, elem_nums = [], []
test_data = random.sample(xrange(100000), END_N)
for i in xrange(START_N, END_N):
loop_test_data = test_data[:i]
elapsed = test(sort, loop_test_data)
timings.append(elapsed)
elem_nums.append(len(loop_test_data))
print "%f s --- %d elems" % (elapsed, len(loop_test_data))
plt.plot(elem_nums, timings)
plt.show()
run_test()
Per quanto posso vedere tutto è OK e dovrei ottenere una curva log N N * bello come un risultato. Ma il quadro si differenzia un po ':
http://s8.postimage.org/o8ysrafat/deque_long_run_2.png
Le cose che ho cercato di esaminare la questione:
- PyPy. La curva è ok
- Disabilitato il GC utilizzando il modulo gc. Ipotesi sbagliata L'output di debug ha mostrato che non funziona nemmeno fino alla fine del test.
- Profili di memoria con melia - niente di speciale o sospetto. ` Ho avuto un'altra implementazione (una ricorsiva che utilizza la stessa funzione di unione), agisce in modo simile. Più cicli di test completi creo - più "salti" ci sono nella curva.
Quindi, come può essere spiegato questo comportamento e, si spera, risolto?
UPD: cambiato liste per collections.deque
UPD2: aggiunto il codice di prova completa
UPD3: io uso Python 2.7.1 su un sistema operativo Ubuntu 11.04, utilizzando un notebook 2Hz quad-core. Ho provato a cambiare la maggior parte degli altri processi: il numero di picchi è diminuito, ma almeno uno di essi era ancora lì.
Si sta usando '.pop (0)' in una lista. Anche se non sono sicuro che questa sia la ragione di questo particolare problema di runtime, è estremamente sub-ottimale: le liste sono implementate come array in CPython e se rimuovi il primo elemento, l'intera cosa deve essere spostata (è un 'O (n) 'operazione). Dovresti fare un salto alla fine o usare un elenco collegato come 'collections.deque' –
Stai guardando pochissimi elementi. Per ottenere una stima utile del runtime asintotico, avrai bisogno di numeri più grandi. –
Come hai fatto i tempi? –