2012-12-04 10 views
11

Possiedo alcuni file di dati molto grandi e per motivi aziendali devo eseguire una vasta manipolazione delle stringhe (sostituendo caratteri e stringhe). Questo è inevitabile. Il numero di sostituzioni si estende a centinaia di migliaia.PHP - Manipolazione stringa lenta

Richiede più tempo di quanto vorrei. PHP è generalmente molto veloce, ma sto facendo così molte di queste manipolazioni delle stringhe che sta rallentando e l'esecuzione dello script è in esecuzione in pochi minuti. Questo è un problema perché lo script viene eseguito frequentemente.

Ho fatto alcuni test e ha scoperto che str_replace è più veloce, seguito da strstr, seguito da preg_replace. Ho anche provato singole istruzioni str_replace e anche la costruzione di matrici di pattern e sostituzioni.

Mi sto prendendo gioco dell'idea di isolare l'operazione di manipolazione delle stringhe e scrivere in una lingua diversa, ma non voglio investire tempo in questa opzione solo per scoprire che i miglioramenti sono trascurabili. Inoltre, conosco solo Perl, PHP e COBOL, quindi per qualsiasi altra lingua dovrei prima impararlo.

Mi chiedo in che modo altre persone hanno affrontato problemi simili?

Ho cercato e non credo che questo duplica qualsiasi domanda esistente.

+0

questo è grande domanda. +1 da me. Vedo che stai usando i file. Puoi passare al database in qualche modo? se no, possiamo vedere alcuni dati dai file? – pregmatch

+0

Come vengono lette e gestite le stringhe? Hai confrontato la sostituzione della stringa con l'effettiva apertura di file o flussi? Piattaforma? – Daniel

+0

Non c'è niente di speciale nei file, è solo una questione di sostituzione dei caratteri di sottolineatura, rimozione di virgole, sostituzione di caratteri non UTF8, ecc. –

risposta

1

Bene, considerando che in PHP alcune operazioni String sono più veloci delle operazioni su array e non si è soddisfatti della sua velocità, è possibile scrivere un programma esterno come si è detto, probabilmente in un linguaggio "di livello inferiore". Consiglierei C/C++.

+0

prima di iniziare a scrivere un proprio sostituitore di stringhe in C Daremo un'occhiata agli strumenti esistenti come 'sed' –

1

Ci sono due modi di gestire questo, IMO:

  • [semplici] Precompute alcune sostituzioni generiche in un processo in background e memorizzarli in un file/DB (questo trucco viene da una gamedev, dove tutti i seni e i seni sono precalcolati una volta e quindi memorizzati nella RAM). Tuttavia, qui puoi facilmente imbatterti nella maledizione della dimensionalità;
  • [non così facile] Implementare lo strumento di sostituzione in C++ o altro linguaggio di programmazione veloce e compilabile e utilizzarlo in seguito. Sphinx è un buon esempio di strumento di manipolazione veloce su grandi set di dati testuali implementati in C++.
+0

Anche se ho pre-calcolato, non avrei ancora dovuto fare una sorta di ricerca in fase di esecuzione per trovare il valore precalcolato appropriato che volevo per ogni ricerca/sostituzione? –

+0

@Simon Roberts, difficile da dire senza alcuni esempi di dati e sostituzioni da applicare. Supponiamo che tu abbia il tuo data.csv. Quindi precalcoli i file come data-no-underscore.csv, data-no-comma.csv, data-no-umlaut.csv, data-no-comma-underscores.csv, data-no-umlaut-underscores.csv ecc. La ricerca di un file necessario è quindi un'attività di concatenazione, diciamo, di implosione alfabetica dei filtri su un nome di set di dati desiderato. –

0

Se è necessario eseguire questa operazione una sola volta e si deve sostituire con contenuto statico, è possibile utilizzare Dreamwaver o un altro editor, quindi non è necessario il PHP. Sarà molto più veloce.

Ancora, se si ha bisogno di fare questo in modo dinamico con PHP (è necessario record del database o altri) è possibile utilizzare i comandi della shell attraverso exec-google search for search-replace

+1

Questo presuppone che i file siano disponibili localmente. – Daniel

+1

Immagino che significherebbe creare matrici di dati per passare a funzioni esterne e analizzare l'input restituito. È probabile che sia più veloce di fare tutto nello script PHP, in primo luogo? –

+0

Invece di str_replace ("mike", "george", $ stringa) è possibile sostituire un intero file; questo significa molto – vectorialpx

1

Se desideri permettere la sostituzione da trattare su più esecuzioni , è possibile creare uno script che elabora ciascun file, creando temporaneamente file sostitutivi con contenuti duplicati. Ciò consentirebbe di estrarre i dati da un file a un altro, elaborare la copia e quindi unire le modifiche oppure, se si utilizza un buffer di flusso, è possibile ricordare ogni riga in modo che il passaggio di copia/unione possa essere saltato.

Il problema potrebbe essere che si elabora un file senza completarlo, rendendolo misto. Pertanto un file temporaneo è adatto.

Ciò consentirà che lo script venga eseguito tutte le volte che sono ancora apportate modifiche, tutto ciò che serve è un file temporaneo che ricordi quali file sono stati elaborati.

+0

Capisco cosa intendi, ma il mio flusso di processo è di base: ottieni file -> elabora -> inserisci i dati nel DB. Penso che questo significhi che devo elaborare l'intero file prima di passare al passaggio successivo. Anche se avessi diviso l'operazione di ricerca/sostituzione, avrei comunque dovuto attendere il completamento di tutte le parti prima di procedere (credo). –

1

Il fattore limitante riguarda il PHP che ricostruisce le stringhe. Considerare:

$out=str_replace('bad', 'good', 'this is a bad example'); 

È un'operazione relativamente basso costo per localizzare 'cattivo' nella stringa, ma per fare spazio per la sostituzione, PHP poi deve spostarsi verso l'alto, ciascuno dei chars e, l, p , m, a, x, e, lo spazio prima di scrivere nel nuovo valore.

Il passaggio degli array per l'ago e il pagliaio migliorerà le prestazioni, ma non quanto potrebbe.

AFAIK, PHP non ha funzioni di accesso alla memoria di basso livello, quindi una soluzione ottimale dovrebbe essere scritta in una lingua diversa, dividendo i dati in "pagine" che possono essere allungate per adattarsi ai cambiamenti. Puoi provare questo usando chunk_split per dividere la stringa in unità più piccole (quindi ogni sostituzione richiederebbe meno giocoleria nella memoria).

Un altro approccio sarebbe quello di scaricarlo in un file e utilizzare sed (questo ancora gestisce una ricerca/sostituzione alla volta), ad es.

sed -i 's/good/bad/g;s/worse/better/g' file_containing_data 
+1

Ah, questo spiega bene perché le ricerche sono molto più veloci delle sostituzioni, grazie. È un peccato che non possa prevedere le modifiche necessarie o che potrei fare una ricerca e una riassegnazione variabile che presumo sarebbe più veloce. –

0

È possibile che tu abbia colpito un muro con PHP. PHP è ottimo, ma in alcune aree fallisce, come l'elaborazione di un sacco di dati. Ci sono alcune cose che potresti fare:

  1. Utilizzare più di un processo php per eseguire l'operazione (2 processi potenzialmente potrebbero richiedere la metà del tempo).
  2. Installare una CPU più veloce.
  3. Esegui l'elaborazione su più macchine.
  4. Utilizzare un linguaggio compilato per elaborare i dati (Java, C, C++, ecc)
+0

So come scrivere PERL multi-thread, ma non PHP. Domande su quell'argomento su questo forum suggeriscono che PHP non lo supporta e la gente sta usando soluzioni alternative che suddividono i file ecc. Macchine multiple non sono un'opzione per me, ma in ogni caso finirei con gli stessi problemi che provo a schivare un PHP multi-thread mi aspetto. Una lingua diversa sembra l'opzione migliore per me. –

+0

PHP non supporta realmente i thread, quindi, quello che faresti è all'interno di php do: qualcosa come 'exec (" php /myFile.php pram1 param2>/dev/null 2> &1 &"); "si otterrebbero in questo modo nel file: '$ param1 = $ arg [1];' –

0

Penso che la domanda è il motivo per cui sono in esecuzione questo script di frequente? Stai eseguendo i calcoli (le stringhe sostituite) sugli stessi dati più e più volte, o lo stai facendo su dati diversi ogni volta?

Se la risposta è la prima, non c'è molto altro da fare per migliorare le prestazioni sul lato PHP. È possibile migliorare le prestazioni in altri modi, ad esempio utilizzando hardware migliore (SSD per letture/scritture più veloci sui file), CPU multicore e suddividendo i dati in parti più piccole eseguendo più script contemporaneamente per elaborare i dati contemporaneamente e RAM più veloce (ovvero velocità bus più elevate).

Se la risposta è la seconda, allora si potrebbe prendere in considerazione la memorizzazione nella cache il risultato utilizzando qualcosa di simile (negozi di cache chiave/valore) memcached o REDDIS modo che si può eseguire solo il calcolo una volta e poi è solo una lineare letto dalla memoria , che è molto economico e non richiede praticamente alcun sovraccarico della CPU (, potresti anche utilizzare la cache della CPU a questo livello).

La manipolazione delle stringhe in PHP è già a buon mercato perché le stringhe di PHP sono essenzialmente solo array di byte. Non c'è praticamente nessun sovraccarico da parte di PHP nel leggere un file in memoria e archiviarlo in una stringa.Se hai qualche codice di esempio che dimostra dove stai vedendo problemi di prestazioni e alcuni numeri di benchmark, potrei avere un consiglio migliore, ma al momento sembra che tu abbia bisogno di rifattorizzare il tuo approccio in base a quali sono le tue esigenze di fondo.

Ad esempio, i costi di CPU e I/O vengono considerati singolarmente quando si utilizzano dati in situazioni diverse. I/O implica il blocco poiché si tratta di una chiamata di sistema. Ciò significa che la tua CPU deve attendere che altri dati arrivino sul filo (mentre il tuo disco trasferisce i dati in memoria) prima che possa continuare a elaborare o calcolare quei dati. La tua CPU sarà sempre molto più veloce della memoria e la memoria è sempre molto più veloce del disco.

Ecco un punto di riferimento semplice per mostrare la differenza:

/* First, let's create a simple test file to benchmark */ 
file_put_contents('in.txt', str_repeat(implode(" ",range('a','z')),10000)); 

/* Now let's write two different tests that replace all vowels with asterisks */ 

// The first test reads the entire file into memory and performs the computation all at once 

function test1($filename, $newfile) { 
    $start = microtime(true); 
    $data = file_get_contents($filename); 
    $changes = str_replace(array('a','e','i','o','u'),array('*'),$data); 
    file_put_contents($newfile,$changes); 
    return sprintf("%.6f", microtime(true) - $start); 
} 

// The second test reads only 8KB chunks at a time and performs the computation on each chunk 

function test2($filename, $newfile) { 
    $start = microtime(true); 
    $fp = fopen($filename,"r"); 
    $changes = ''; 
    while(!feof($fp)) { 
     $changes .= str_replace(array('a','e','i','o','u'),array('*'),fread($fp, 8192)); 
    } 
    file_put_contents($newfile, $changes); 
    return sprintf("%.6f", microtime(true) - $start); 
} 

Le due prove precedenti fanno la stessa cosa esatta, ma Test2 si rivela significativamente più veloce per me quando sto usando piccole quantità di dati (circa 500 KB in questo test).

Ecco il punto di riferimento è possibile eseguire ...

// Conduct 100 iterations of each test and average the results 
for ($i = 0; $i < 100; $i++) { 
    $test1[] = test1('in.txt','out.txt'); 
    $test2[] = test2('in.txt','out.txt'); 
} 
echo "Test1 average: ", sprintf("%.6f",array_sum($test1)/count($test1)), "\n", 
    "Test2 average: ", sprintf("%.6f\n",array_sum($test2)/count($test2)); 

Per me il punto di riferimento di cui sopra dà Test1 average: 0.440795 e Test2 average: 0.052054, che è un ordine di grandezza differenza e questo è solo la prova su 500KB di dati. Ora, se aumento la dimensione di questo file a circa 50MB Test1 in realtà si dimostra più veloce poiché ci sono meno chiamate I/O di sistema per iterazione (ovvero stiamo leggendo dalla memoria linearmente in Test1), ma più Costo della CPU (ovvero stiamo eseguendo un calcolo molto più grande per iterazione). Generalmente la CPU si dimostra in grado di gestire quantità di dati molto più elevate alla volta rispetto a quanto i dispositivi I/O possono inviare sul bus.

Quindi nella maggior parte dei casi non è una soluzione valida per tutti.

+0

Se i valori saranno uguali o diversi ogni volta che non è noto, è necessario un test ogni volta. Ho provato a valutare prima di sostituire dopo aver letto la risposta di symbbean ma in realtà un po 'aumentato il tempo di elaborazione - presumibilmente perché lo stesso str_replace contiene un test di corrispondenza quindi stavo testando due volte 1. Il tempo di accesso ai file non è un problema (millisecondi) .I file completi sono inglobati negli array prima dell'elaborazione. di str_replace, non le stringhe leggere direttamente dai file. Grazie per la risposta dettagliata –

0

Poiché si conosce Perl, suggerirei di eseguire le manipolazioni di stringa in perl utilizzando le espressioni regolari e utilizzare il risultato finale nella pagina Web di PHP.

Questo sembra migliore per i seguenti motivi

  1. Sapete già Perl
  2. Perl fa di elaborazione delle stringhe meglio

È possibile utilizzare PHP dove solo necessario.

+0

Sì, penso che questa sarà una buona opzione per me. o eseguire alcuni test e vedere quanto è più rapido il PERL. –

0

questa manipolazione deve avvenire al volo? in caso contrario, potrei suggerire la pre-elaborazione ... forse tramite un cron job.

definire quali regole verranno utilizzate. è solo uno str_replace o alcuni diversi? devi fare l'intero file in un colpo solo? o puoi dividerlo in più lotti? (ad esempio metà del file alla volta)

una volta definite le regole, decidi quando eseguire l'elaborazione. (ad esempio prima che tutti possano mettersi al lavoro)

quindi è possibile impostare una coda di lavoro. Ho usato i lavori di cron di Apache per eseguire i miei script php in un determinato orario.

per un progetto ho lavorato qualche tempo fa ho avuto una configurazione come questa:

7:00 - pull 10,000 records from mysql and write them to 3 separate files. 
7:15 - run a complex regex on file one. 
7:20 - run a complex regex on file two. 
7:25 - run a complex regex on file three. 
7:30 - combine all three files into one. 
8:00 - walk into the metting with the formatted file you boss wants. *profit* 

speranza che questo aiuta farti pensare ...