Durante il tentativo di rispondere a What is the preferred way to compose a set from multiple lists in Python, ho eseguito alcune analisi delle prestazioni e ho trovato una conclusione piuttosto sorprendente.Perché creare un set da un elenco concatenato più velocemente di usare `.update`?
Utilizzando
python -m timeit -s '
import itertools
import random
n=1000000
random.seed(0)
A = [random.randrange(1<<30) for _ in xrange(n)]
B = [random.randrange(1<<30) for _ in xrange(n)]
C = [random.randrange(1<<30) for _ in xrange(n)]'
per la configurazione, ho cronometrato i seguenti frammenti:
> $TIMEIT 'set(A+B+C)'
10 loops, best of 3: 872 msec per loop
> $TIMEIT 's = set(A); s.update(B); s.update(C)'
10 loops, best of 3: 930 msec per loop
> $TIMEIT 's = set(itertools.chain(A,B,C))'
10 loops, best of 3: 941 msec per loop
Con mia grande sorpresa, set(A+B+C)
è la più veloce nonostante il fatto che essa crea una lista intermedia contenente 3000000 elementi . .update
e itertools.chain
sono entrambi più lenti, anche se nessuno dei due copia alcuna lista.
Cosa sta succedendo qui?
EDIT: su una seconda macchina (OS X 10.10.5, Python 2.7.10, 2.5GHz Core i7), ho eseguito il seguente script (che esegue i test in avanti e indietro per evitare gli effetti di ordinazione):
SETUP='import itertools
import random
n=1000000
random.seed(0)
A = [random.randrange(1<<30) for _ in xrange(n)]
B = [random.randrange(1<<30) for _ in xrange(n)]
C = [random.randrange(1<<30) for _ in xrange(n)]'
python -m timeit -s "$SETUP" 'set(A+B+C)'
python -m timeit -s "$SETUP" 's = set(A); s.update(B); s.update(C)'
python -m timeit -s "$SETUP" 's = set(itertools.chain(A,B,C))'
python -m timeit -s "$SETUP" 's = set(itertools.chain(A,B,C))'
python -m timeit -s "$SETUP" 's = set(A); s.update(B); s.update(C)'
python -m timeit -s "$SETUP" 'set(A+B+C)'
e ottenuti i seguenti risultati:
10 loops, best of 3: 579 msec per loop
10 loops, best of 3: 726 msec per loop
10 loops, best of 3: 775 msec per loop
10 loops, best of 3: 761 msec per loop
10 loops, best of 3: 737 msec per loop
10 loops, best of 3: 555 msec per loop
Ora set(A+B+C)
è chiaramente più veloce, ed i risultati sono abbastanza Stabl e - è difficile calcolarlo fino al semplice errore di misura. L'esecuzione ripetuta di questo script produce risultati simili.
L'unica ipotesi che posso fare è che il primo caso passa in un elenco che ha una lunghezza nota, e quindi forse la costruzione dell'insieme può scegliere in modo più sensibile il requisito di memoria iniziale sottostante, in contrapposizione agli altri due in cui l'insieme viene creato e ridimensionato due volte (secondo caso) o creato con un iteratore dove potenzialmente ridimensiona Inter nally molte volte. –
A meno che non abbiano cambiato 'set_init', non è così che sembra funzionare. ['set_init'] (http://svn.python.org/projects/python/trunk/Objects/setobject.c) chiama direttamente a' set_update_internal', che scorre semplicemente sugli elementi. (Vorrei tirare da 'hg.python.org' ma quel server sembra inattivo al momento) correlato a – nneonneo
: [Combinare due liste ordinate in Python] (http://stackoverflow.com/a/482848/4279) – jfs