2012-01-03 13 views
14

Stavo leggendo a useful post at WRI blog on improving speed of code, e ho bisogno di aiuto per capire questo.differenza di velocità nel fare una tabella

confrontare questi costi

Timing[ 
tbl = Table[i + j, {i, 1, 1000}, {j, 1, 1000}];  
] 

{0.031, Null} 

e

Timing[ 
a = 1000; 
tbl = Table[i + j, {i, 1, a}, {j, 1, a}]; 
] 

{0.422, Null} 

Quindi è molto più veloce quando mettere il valore effettivo per il limite all'interno della tabella stessa vs esterno. La spiegazione per questo, che sono sicuro sia corretta, ma ho bisogno di aiuto nella comprensione, è che Table è compilato se il suo limite è numerico o no, questo perché i suoi Attributi sono HoldAll.

Ma la mia domanda è: come funzionerebbe il precedente, perché i limiti a Table devono, a un certo punto, diventare comunque numerici? Non riesco a scrivere

Clear[a] 
tbl = Table[i + j, {i, 1, a}, {j, 1, a}] 

Quanto sopra dà un errore.

Quindi, per me, la scrittura a=1000 fuori Table vs interno, dovrebbe aver fatto alcuna differenza, poiché senza a avere un valore numerico, Table[] non possono fare nulla. Quindi la sostituzione di a con il numero 1000 deve avvenire in un punto dal valutatore prima che lo Table[] possa fare qualcosa di utile, non sarebbe?

In altre parole, quello che Table dovrebbe vedere, alla fine, è {i, 1, 1000}, {j, 1, 1000} in entrambi i casi.

Quindi, il modo in cui ho pensato che questo sarebbe accaduto è questo:

  1. Evaluator sostituisce a per 1000 negli argomenti della tavola
  2. Evaluator chiama Table con il risultato, che è ora tutto numerica.
  3. Tabella Compila e ora è più veloce.

Ma quello che sembra succedere è qualcos'altro. (a causa di HoldAll?)

  1. La tabella prende i suoi argomenti, così com'è. Poiché ha HoldAll, quindi vede a e non 1000.
  2. Non chiama Compile poiché i suoi argomenti non sono tutti i numeri.
  3. Ora generare una tabella con il limite a, valutatore restituisce a a 1000
  4. Tabella viene generato adesso tutti i limiti sono numerici, ma più lento ora poiché il codice non viene compilato.

La domanda è: cosa succede sopra? Qualcuno potrebbe spiegare i passi che sarebbero accaduti per spiegare questa differenza nei tempi?

Inoltre, come si potrebbe assicurare che la tabella è stata compilata in entrambi i casi nell'esempio precedente, anche se si utilizza una variabile per il limite?Non è sempre possibile codificare i numeri per i limiti del tavolo, ma a volte si deve usare una variabile per questi. Si dovrebbe usare esplicitamente il comando Compile? (Non uso direttamente lo Compile, poiché presumo che venga eseguito automaticamente quando necessario).

di modifica (1)

In risposta al post di Mike in basso a trovare alcuna differenza nei tempi quando si utilizza una chiamata.

ClearAll[tblFunc]; 
Timing[a = 1000; 
tblFunc[a_] := Table[i + j, {i, 1, a}, {j, 1, a}]; 
Developer`PackedArrayQ[tblFunc[a]] 
] 

{0.031, True} 

Ma questo è perché a è ora il numero 1000 all'interno della funzione, una volta che è chiamato. Poiché M passa le cose per VALUE.

Se si forza la chiamata ad essere per riferimento, in modo che a è lasciato non valutata, allora otteniamo

ClearAll[tblFunc]; 
Timing[a = 1000; 
tblFunc[a_] := Table[i + j, {i, 1, a}, {j, 1, a}]; 
Developer`PackedArrayQ[tblFunc[[email protected]]] 
] 

ora vediamo il risultato atteso, dal momento che ora a è ancora simbolica all'interno della funzione, siamo torna al punto di partenza, e ora è lento, dal momento che non è imballato. E poiché non è impacchettato, Compile non viene utilizzato.

{0.437, False} 

modifica (2) Grazie a tutti per le risposte, penso che ho imparato da loro assegnare.

Ecco un riepilogo esecutivo, solo per essere sicuro di avere tutto ok.

enter image description here

modifica (3)

Ecco i link che ho particolarmente legati alla suggerimenti da utilizzare per rendere il codice di Mathematica corre più veloce.

  1. http://library.wolfram.com/howtos/faster/
  2. http://blog.wolfram.com/2011/12/07/10-tips-for-writing-fast-mathematica-code/
  3. https://stackoverflow.com/questions/4721171/performance-tuning-in-mathematica
  4. Using Array and Table Functions in Mathematica. Which is best when
+4

Nota che puoi avere cose come 'Tabella [i + j, {i, 1, 1000}, {j, 1, i}]' che è un motivo per 'Tabella' da non precompilare quando i limiti non sono tutti i numeri. –

+1

Domanda correlata: http://stackoverflow.com/questions/5764774/using-array-and-table-functions-in-mathematica-which-is-best-when/ –

+4

@David Sono d'accordo con la tua affermazione, voglio solo sottolinea che la vera ragione per cui 'Table' è lenta per gli iteratori simbolici sembra essere che non può determinare se il risultato sarà o meno un array * rettangolare * e, quindi, non può usare matrici compresse. Il fatto che non possa quindi usare 'Compile' ne deriva - perché gli array compressi sono ciò che dà a' Compile 'i suoi guadagni in termini di efficienza (almeno quando si compila su MVM target, che è quello che credo stia accadendo in auto-compilation). –

risposta

16

Quindi questo è quello che penso sta accadendo. Il motivo per cui viene visualizzato il rallentamento tra un limite numerico e un limite simbolico su Table è dovuto al fatto che si esegue un doppio indice. Ogni sotto-tabella (ad esempio, passando sopra tutti gli indici j per un indice fisso i) viene costruita separatamente e quando il limite è simbolico, vi è un ulteriore passaggio nel determinare quel limite prima di costruire ciascun sottotabella. Puoi vedere questo esaminando, ad es.

Trace[a = 3; 
     tbl = Table[i + j, {i, 1, a}, {j, 1, a}]; 
    ] 

David dà un buon esempio per il motivo per cui si vorrebbe fare questo controllo per ogni lista sub. Sul perché Mathematica non riesco a capire quando questo controllo non è necessario Non ho idea.Se si dispone di un solo indice per riassumere sopra non v'è alcuna differenza di velocità tra la versione simbolica e numerica

Timing[tbl = Table[i + j, {j, 1, 1000}];] 
{0.0012, Null} 

Timing[a = 1000; 
     tbl = Table[i + j, {j, 1, a}]; 
     ] 
{0.0013, Null} 

Per rispondere alla tua follow-up per quanto riguarda la velocità; rendendo tbl una funzione è più veloce sia per i limiti numerici che simbolici.

Timing[a = 1000; 
     tblFunc[a_] := Table[i + j, {i, 1, a}, {j, 1, a}]; 
     tblFunc[a]; 
     ] 

{0.045171, Null} 

vs.

Timing[tbl = Table[i + j, {i, 1, 1000}, {j, 1, 1000}];] 
{0.066864, Null} 

Timing[a = 1000; 
     tbl = Table[i + j, {i, 1, a}, {j, 1, a}]; 
     ] 
{0.632128, Null} 

Si guadagna ancora di più velocità, se si ha intenzione di riutilizzare la costruzione tbl.

+3

Penso che tblFunc sia veloce perché il SetDelayed che definisce la funzione si comporta come un With quando viene valutato tblFunc, es. a viene sostituito dal suo valore numerico prima di valutare l'espressione rhs. Quindi possiamo avere il limite come argomento simbolico, ma torniamo ancora al caso dell'argomento numerico. – faysou

+0

@ Faysal, penso che tu abbia ragione. Sostituendo il secondo iteratore con '{j, 1, i}' in 'tblFunc' si ottiene un tempo di 0.34 che è di nuovo alla pari con la versione simbolica non funzionale (anche se un po 'più veloce). – Timo

3

Le cose principali da monitorare, come altri hanno menzionato, sono il riempimento e la lunghezza dell'elenco. Io in realtà non vedo le differenze che Timo riporta:

ClearAll[tblFunc]; 
Timing[a = 1000; 
tblFunc[a_] := Table[i + j, {i, 1, a}, {j, 1, a}]; 
Developer`PackedArrayQ[tblFunc[a]]] 

{0.077706, True} 

vs 

ClearAll[tbl]; 
Timing[ 
tbl = Table[i + j, {i, 1, 1000}, {j, 1, 1000}]; 
Developer`PackedArrayQ[tbl]] 

{0.076661, True} 

ClearAll[tbl]; 
Timing[a = 1000; 
tbl = Table[i + j, {i, 1, a}, {j, 1, a}]; 
Developer`PackedArrayQ[tbl]] 

{1.02879, False} 

Quindi per me l'unica differenza è che se l'elenco è pieno. Che si tratti di una funzione non fa differenza per i tempi sul mio set up. E come previsto quando si spegne autocompilation i tempi sono gli stessi per tutto quanto sopra, perché non si verifica alcun imballaggio:

SetSystemOptions["CompileOptions" -> {"TableCompileLength" -> Infinity}]; 

{1.05084, False} 

vs 

{1.00348, False} 

{1.01537, False} 

ripristinare la lunghezza della tabella di autocompilazione:

SetSystemOptions["CompileOptions" -> {"TableCompileLength" -> 250}] 
+0

Ma non è la ragione per cui non si è trovata alcuna differenza nel timing perché quando si effettua una chiamata a una funzione, quindi all'interno della funzione, 'a' ora è un numero? poiché la chiamata M è per VALUE? Per favore, vedi modifica (1) nel mio post per quello che intendo. THanks – Nasser

+0

Stavo solo riproducendo il codice di Timo e notando che non vedo le differenze temporali che vede. La ragione sembra essere che entrambi sono pieni - quindi sono d'accordo con il tuo punto. –

2

Questo è un po 'OT, ma per velocità qui si potrebbe desiderare di evitare l'utilizzo dell'elaborazione articolo per articolo implicita nell'uso di Table. Piuttosto, usa Esterno. Ecco cosa vedo sul mio sistema:

Timing[Outer[Plus, Range[5000], Range[5000]];] 
{0.066763,Null} 

    Timing[Table[i + j, {i, 1, 5000}, {j, 1, 5000}];] 
{0.555197,Null} 

Una differenza drammatica.

+0

Osservazione interessante! Credo che ciò sia dovuto a un involucro speciale di "Outer" per alcuni argomenti comuni come "Plus" o "Times". La mia impressione è che per una funzione generale, 'Table' è più probabile che sia più veloce in quanto potrebbe compilare. Un altro punto dati interessante: 'Timing [Tabella [i + j, {i, Range [5000]}, {j, Range [5000]}];]' -> 16 secondi nella mia macchina (cioè non compilato) – Szabolcs

+0

I tempi Ho ottenuto sono per Mathematica 8.0.4 sotto OS X 10.6.8 su un iMac con un Core i7 da 3,4 GHz, 16 GB di RAM. E un nuovo kernel per ogni valutazione. – murray

+0

Il mio punto era che l'analogo più preciso di 'Outer' usando' Table' può sembrare piuttosto lento. 'Esterno [Plus, Range [5000], Range [5000]' è molto veloce (solo per 'Plus' e funzioni simili),' Tabella [i + j, {i, 5000}, {j, 5000}] 'e 'Con [{r = Range [5000]}, Table [i + j, {i, r}, {j, r}]] sono veloci, mentre' Table [i + j, {i, Range [5000] }, {j, Range [5000]}] 'è piuttosto lento. Secondo le altre risposte a questa domanda, questo è perché (la mancanza di) auto-compilazione. La mia ipotesi sul motivo per cui 'Outer' è così veloce è che è speciale (ottimizzato) per' Plus'. – Szabolcs

Problemi correlati