2011-01-12 15 views
7

Questa è una semplice domanda di programmazione, derivante dalla mia scarsa conoscenza di come PHP gestisce la copia e il disinserimento di array durante un ciclo foreach. È così, ho una matrice che mi viene da una fonte esterna formattata nel modo in cui voglio cambiare. Un semplice esempio potrebbe essere:Il disinserimento dei valori dell'array durante l'iterazione salva in memoria?

$myData = array('Key1' => array('value1', 'value2')); 

Ma quello che voglio sarebbe qualcosa di simile:

$myData = array([0] => array('MyKey' => array('Key1' => array('value1', 'value2')))); 

così prendo il primo $myData e formattarla come la seconda $myData. Sto perfettamente bene con il mio algoritmo di formattazione. La mia domanda sta nel trovare un modo per conservare la memoria dato che questi array potrebbero diventare un po 'ingombranti. Quindi, durante il mio ciclo foreach copio i valori dell'array corrente nel nuovo formato, quindi annullo il valore con cui sto lavorando dall'array originale. Es .:

$formattedData = array(); 
foreach ($myData as $key => $val) { 
    // do some formatting here, copy to $reformattedVal 

    $formattedData[] = $reformattedVal; 

    unset($myData[$key]); 
} 

è la chiamata alla unset() una buona idea qui? Ad esempio, conserva memoria dato che ho copiato i dati e non ho più bisogno del valore originale? Oppure, PHP esegue automaticamente la raccolta dei dati poiché non faccio riferimento in alcun codice successivo?

Il codice funziona correttamente e finora i miei set di dati sono stati di dimensioni troppo ridotte per verificare le differenze di prestazioni. Solo non so se mi sto preparando per alcuni bug strani o successi della CPU in seguito.

Grazie per eventuali approfondimenti.
-sR

+0

A meno che le strutture dei dati non siano assolutamente enormi (una grande parte della RAM), non ti preoccupi di nulla. Se php esaurisce una memoria, te lo dirà, e puoi aumentarla in php.ini. – Ian

+4

È un'idea * stupida *. Hai appena introdotto un effetto collaterale che potrebbe essere dimenticato in seguito per qualche * micro-ottimizzazione *: -/E no, PHP (e nessun altro linguaggio GC standard che conosco) è in grado di rendere i dati * contenuti * in un struttura dati disponibile per il recupero mentre esiste un riferimento al * container * (questo esclude nozioni come riferimenti soft/deboli). Il 'unset' può/causerà l'avvio del GC di PHP, ma le prestazioni effettive ottenute - se del caso - a causa della pressione della memoria rilasciata non sono banali da generalizzare. Se questo * diventa * un problema, * allora * indirizzarlo. –

+0

qual è la dimensione di questo array? –

risposta

0

A meno che non si stia accedendo all'elemento per impostazione di riferimento, non si farà assolutamente nulla, poiché non è possibile modificare l'array durante l'iteratore.

Detto questo, in genere si considera una cattiva pratica modificare la raccolta su cui si sta iterando - un approccio migliore sarebbe quello di suddividere l'array sorgente in blocchi più piccoli (caricando solo una porzione dei dati di origine alla volta) ed elaborarli, disinserendo ogni "array" dell'intero array man mano che si procede.

+0

"unsetting will do nothing nothing" - questo non è corretto, il suo codice disinserirà la variabile dall'array originale – Andy

+0

@Andy Ho affermato chiaramente che non farà nulla se è ** non accessibile per riferimento **. Dal manuale PHP - "A meno che l'array non faccia riferimento, foreach opera su una copia dell'array specificato e non sull'array stesso." –

+0

Correggere, ma noterai che il suo codice sta disabilitando la variabile dall'array originale, non dalla copia. – Andy

4

Utilizzare un riferimento alla variabile nel ciclo foreach utilizzando l'operatore &. Ciò evita di fare una copia dell'array in memoria per foreach per iterare su.

modifica: come sottolinea Artefacto disinserimento variabile diminuisce solo il numero di riferimenti alla variabile originale, così la memoria salvata è solo su puntatori piuttosto che il valore della variabile. Bizarramente utilizzando un riferimento aumenta effettivamente l'utilizzo della memoria totale, presumibilmente il valore viene copiato in una nuova posizione di memoria anziché essere referenziato.

meno che la matrice viene fatto riferimento, foreach opera su una copia dell'array specificato e non l'array stessa. foreach ha alcuni effetti collaterali sul puntatore dell'array. Non fare affidamento su il puntatore dell'array durante o dopo lo foreach senza reimpostarlo.

Utilizzare memory_get_usage() per identificare la quantità di memoria utilizzata.

C'è una buona annotazione sull'utilizzo della memoria e l'assegnazione here.

Questo è un codice di prova utile per visualizzare l'allocazione della memoria: provare a decommentare le righe commentate per vedere l'utilizzo totale della memoria in diversi scenari.

echo memory_get_usage() . PHP_EOL; 
$test = $testCopy = array(); 
$i = 0; 
while ($i++ < 100000) { 
    $test[] = $i; 
} 
echo memory_get_usage() . PHP_EOL; 
foreach ($test as $k => $v) { 
//foreach ($test as $k => &$v) { 
    $testCopy[$k] = $v; 
    //unset($test[$k]); 
} 
echo memory_get_usage() . PHP_EOL; 
+0

Grazie per la risposta e le informazioni utili Utilizzando il tuo esempio di codice, sto vedendo un diff 5MB di memoria durante l'uso di 'unset()'. Inoltre, l'utilizzo della memoria va * su * quando si fa riferimento alla matrice nel foreach (mentre non si usa 'unset()'). Interessante ... sebbene ci sia abbastanza tempo da dedicare – Soulriser

+0

Non proprio corretto, vedere la risposta di Artefacto qui sotto! – GTodorov

2

Se in qualsiasi punto del "formattazione" si fa qualcosa di simile:

$reformattedVal['a']['b'] = $myData[$key]; 

poi facendo unset($myData[$key]); è irrilevante memoria-saggio perché si sta solo diminuendo il numero di riferimento della variabile, che ora esiste in due posti (all'interno di $myData[$key] e $reformattedVal['a']['b']). In realtà, si salva la memoria dell'indicizzazione della variabile all'interno dell'array originale, ma questo è quasi nulla.

+0

Questo non è corretto - per variabili di default non vengono passati per riferimento, solo gli oggetti sono – Andy

+1

@Andy Prima nessuno passa niente (vedi qualche funzione?), in secondo luogo, in un incarico di '$ a = $ b' in situazioni normali nessuna memoria viene copiato tra le due variabili (PHP implementa copy-on-write), anche se si comporta come se la memoria fosse stata copiata. – Artefacto

+0

Il mio errore, ho inteso l'assegnazione piuttosto che il passaggio dei parametri. Ho aggiunto un codice di test alla mia risposta per dimostrare la memoria salvata usando 'unset()'. – Andy

3

Si ricorda la rules of Optimization Club:

  1. La prima regola di ottimizzazione club è, non ottimizzare.
  2. La seconda regola di Optimization Club è che non si ottimizza senza misurare.
  3. Se l'app è in esecuzione più veloce del protocollo di trasporto sottostante, l'ottimizzazione è finita.
  4. Un fattore alla volta.
  5. Nessun marketroids, nessun programma marketroid.
  6. Il test durerà il tempo necessario.
  7. Se questa è la tua prima notte in Optimization Club, devi scrivere un caso di test.

Le regole n. 1 e n. 2 sono particolarmente rilevanti qui. A meno che tu non sappia che devi ottimizzare, e a meno che tu non abbia misurato quella necessità di ottimizzazione, allora non farlo. L'aggiunta di unset aggiungerà un hit di runtime e renderà i futuri programmatori il motivo per cui lo stai facendo.

Lascia stare.

+0

cosa significa # 5 – Jason

+0

"Marketroid" indica qualcuno del reparto Marketing. Nel senso più ampio, non lasciare a qualcuno termini dettati non tecnici su ciò che il tuo programma dovrebbe essere in grado di fare. –

+0

Grazie per il riferimento, Andy. Conosco Marketroids fin troppo bene. – Soulriser

2

Stavo esaurendo la memoria durante l'elaborazione di righe di un file di testo (xml) all'interno di un ciclo. Per chiunque abbia una situazione simile, ha funzionato per me:

while($data = array_pop($xml_data)){ 
    //process $data 
}