2015-07-30 66 views
20

Date due variabili stringa $string e $needle in perl, qual è il modo più efficace per controllare se $string inizia con $needle.

  • $string =~ /^\Q$needle\E/ è la corrispondenza più ho potuto pensare che fa ciò che è richiesto, ma è meno efficiente (da lontano) delle soluzioni che ho provato.
  • index($string, $needle) == 0 funziona ed è relativamente efficiente per alcuni valori di $string e $needle ma cerca inutilmente l'ago in altre posizioni (se non trovato all'inizio).
  • substr($string, 0, length($needle)) eq $needle dovrebbe essere abbastanza semplice ed efficiente, ma nella maggior parte dei miei pochi test non è più efficiente del precedente.

C'è un modo canonico per farlo in perl di cui non sarei a conoscenza o in alcun modo per ottimizzare una delle soluzioni di cui sopra?

(nel mio caso d'uso particolare, $string e $needle saranno diversi in ogni esecuzione, quindi precompilare un'espressione regolare non è un'opzione).


Esempio di come misurare le prestazioni di una determinata soluzione (qui da un POSIX sh):

string='somewhat not so longish string' needle='somew' 
time perl -e ' 
    ($n,$string,$needle) = @ARGV; 
    for ($i=0;$i<$n;$i++) { 

    index($string, $needle) == 0 

    }' 10000000 "$string" "$needle" 

Con questi valori, index() comporta meglio substr()+eq con questo sistema con Perl 5.14.2 , ma con:

string="aaaaabaaaaabaaaaabaaaaabaaaaabaaaaab" needle="aaaaaa" 

Questo è invertito.

+1

Le diverse versioni di Perl avranno un effetto qui e io consiglio di aggiungere il codice di riferimento che hai usato per il feedback o il riutilizzo. – Ashley

+0

@Ashley, buon punto, aggiornato. –

+0

Forse scriverai 'String :: MoreUtils :: XS'? – pilcrow

risposta

15

Quanto è importante, davvero? Ho eseguito una serie di benchmark e il metodo index ha una media di 0,68 microsecondi per iterazione; il metodo regex 1.14μs; il metodo substr 0.16μs. Anche i miei scenari peggiori (stringhe di 2250 caratteri equivalenti), index hanno preso 2,4 μs, espressioni regolari hanno preso 5,7 μs e substr hanno richiesto 0,5 μs.

Il mio consiglio è quello di scrivere una routine di libreria:

sub begins_with 
{ 
    return substr($_[0], 0, length($_[1])) eq $_[1]; 
} 

e concentrare i vostri sforzi di ottimizzazione altrove.

AGGIORNAMENTO: in base alle critiche al mio scenario "peggiore" descritto sopra, ho eseguito un nuovo set di benchmark con una stringa generata a caso da 20.000 caratteri, confrontandola con se stessa e con una stringa che differiva solo nel ultimo byte.

Per tali stringhe lunghe, la soluzione regex era di gran lunga la peggiore (una regex di 20.000 caratteri è un inferno): 105μs per il successo della corrispondenza, 100μs per il fallimento della corrispondenza.

Le soluzioni index e substr erano ancora piuttosto veloci. index era 11,83μs/11,86μs per il successo/fallimento e substr era 4,09μs/4,15μs. Spostando il codice in una funzione separata aggiunta circa 0,222 ± 0,05μs.

codice di riferimento disponibili presso: http://codepaste.net/2k1y8e

non so le caratteristiche di @ i dati di Stéphane, ma il mio consiglio stand.

+0

Per ragioni, si può presumere che quella corrispondenza di stringhe sia il punto critico nel mio codice e che siano state fatte tutte le altre ottimizzazioni possibili. A tale proposito, l'utilizzo di una funzione ridurrà solo le prestazioni. Ma soprattutto, la mia speranza nel porre la domanda era che c'era un modo migliore/canonico per farlo in perl. –

+1

Per prima 'perl's, si potrebbe voler [omettere la dichiarazione di ritorno] (http://www.effectiveperlprogramming.com/2014/06/perl-5-20-optimizes-return-at-the-end-of -a-subroutine /). –

+0

@ikegami 'substr' sembra essere il più veloce quando il prefisso non corrisponde perché limita l'estensione della ricerca. –