.net - Come funziona RegexOptions.Compiled?

Translate

Cosa succede dietro le quinte quando contrassegni un'espressione regolare come da compilare? In che modo questo confronta / è diverso da un'espressione regolare memorizzata nella cache?

Utilizzando queste informazioni, come si determina quando il costo del calcolo è trascurabile rispetto all'aumento delle prestazioni?

This question and all comments follow the "Attribution Required."

Tutte le risposte

Translate

RegexOptions.Compiledindica al motore delle espressioni regolari di compilare l'espressione delle espressioni regolari in IL utilizzando la generazione di codice leggera (LCG). Questa compilazione avviene durante la costruzione dell'oggetto epesantementelo rallenta. A loro volta, le corrispondenze che utilizzano l'espressione regolare sono più veloci.

Se non specifichi questo flag, la tua espressione regolare viene considerata "interpretata".

Prendi questo esempio:

public static void TimeAction(string description, int times, Action func)
{
    // warmup
    func();

    var watch = new Stopwatch();
    watch.Start();
    for (int i = 0; i < times; i++)
    {
        func();
    }
    watch.Stop();
    Console.Write(description);
    Console.WriteLine(" Time Elapsed {0} ms", watch.ElapsedMilliseconds);
}

static void Main(string[] args)
{
    var simple = "^\\d+$";
    var medium = @"^((to|from)\W)?(?<url>http://[\w\.:]+)/questions/(?<questionId>\d+)(/(\w|-)*)?(/(?<answerId>\d+))?";
    var complex = @"^(([^<>()[\]\\.,;:\[email protected]""]+"
      + @"(\.[^<>()[\]\\.,;:\[email protected]""]+)*)|("".+""))@"
      + @"((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}"
      + @"\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+"
      + @"[a-zA-Z]{2,}))$";


    string[] numbers = new string[] {"1","two", "8378373", "38737", "3873783z"};
    string[] emails = new string[] { "[email protected]", "[email protected]", "[email protected]", "[email protected]" };

    foreach (var item in new[] {
        new {Pattern = simple, Matches = numbers, Name = "Simple number match"},
        new {Pattern = medium, Matches = emails, Name = "Simple email match"},
        new {Pattern = complex, Matches = emails, Name = "Complex email match"}
    })
    {
        int i = 0;
        Regex regex;

        TimeAction(item.Name + " interpreted uncached single match (x1000)", 1000, () =>
        {
            regex = new Regex(item.Pattern);
            regex.Match(item.Matches[i++ % item.Matches.Length]);
        });

        i = 0;
        TimeAction(item.Name + " compiled uncached single match (x1000)", 1000, () =>
        {
            regex = new Regex(item.Pattern, RegexOptions.Compiled);
            regex.Match(item.Matches[i++ % item.Matches.Length]);
        });

        regex = new Regex(item.Pattern);
        i = 0;
        TimeAction(item.Name + " prepared interpreted match (x1000000)", 1000000, () =>
        {
            regex.Match(item.Matches[i++ % item.Matches.Length]);
        });

        regex = new Regex(item.Pattern, RegexOptions.Compiled);
        i = 0;
        TimeAction(item.Name + " prepared compiled match (x1000000)", 1000000, () =>
        {
            regex.Match(item.Matches[i++ % item.Matches.Length]);
        });

    }
}

Esegue 4 test su 3 diverse espressioni regolari. Per prima cosa verifica un filesingleuna volta fuori partita (compilato vs non compilato). Secondo, verifica le corrispondenze ripetute che riutilizzano la stessa espressione regolare.

I risultati sulla mia macchina (compilati nel rilascio, nessun debugger allegato)

1000 partite singole (costruisci Regex, abbina e dispose)

Type        | Platform | Trivial Number | Simple Email Check | Ext Email Check
------------------------------------------------------------------------------
Interpreted | x86      |    4 ms        |    26 ms           |    31 ms
Interpreted | x64      |    5 ms        |    29 ms           |    35 ms
Compiled    | x86      |  913 ms        |  3775 ms           |  4487 ms
Compiled    | x64      | 3300 ms        | 21985 ms           | 22793 ms

1.000.000 corrispondenze: riutilizzo dell'oggetto Regex

Type        | Platform | Trivial Number | Simple Email Check | Ext Email Check
------------------------------------------------------------------------------
Interpreted | x86      |  422 ms        |   461 ms           |  2122 ms
Interpreted | x64      |  436 ms        |   463 ms           |  2167 ms
Compiled    | x86      |  279 ms        |   166 ms           |  1268 ms
Compiled    | x64      |  281 ms        |   176 ms           |  1180 ms

Questi risultati mostrano che le espressioni regolari compilate possono essere fino a60%più veloce per i casi in cui riutilizzi il fileRegexoggetto.peròin alcuni casi può essere finita3 ordini di grandezzapiù lento da costruire.

Mostra anche che il fileversione x64di .NET può essereDa 5 a 6 volte più lentoquando si tratta di compilare espressioni regolari.


La raccomandazione sarebbe quella diusa la versione compilatanei casi in cui sia

  1. Non ti interessa il costo di inizializzazione degli oggetti e hai bisogno di un aumento delle prestazioni aggiuntivo. (nota che stiamo parlando di frazioni di millisecondo qui)
  2. Ti interessa un po 'il costo di inizializzazione, ma stai riutilizzando l'oggetto Regex così tante volte che lo compenserà durante il ciclo di vita dell'applicazione.

Spanner in lavorazione, la cache Regex

Il motore delle espressioni regolari contiene una cache LRU che contiene le ultime 15 espressioni regolari che sono state testate utilizzando i metodi statici suRegexclasse.

Per esempio:Regex.Replace, Regex.Matchecc. tutti usano la cache Regex.

La dimensione della cache può essere aumentata impostandoRegex.CacheSize. Accetta modifiche alle dimensioni in qualsiasi momento durante il ciclo di vita dell'applicazione.

Le nuove espressioni regolari vengono solo memorizzate nella cachedagli aiutanti staticisulla classe Regex. Se costruisci i tuoi oggetti, la cache viene controllata (per il riutilizzo e il bump), tuttavia, l'espressione regolare che costruisci ènon aggiunto alla cache.

Questa cache è un filebanaleCache LRU, viene implementata utilizzando un semplice doppio elenco collegato. Se ti capita di aumentarlo a 5000 e utilizzare 5000 chiamate diverse sugli helper statici, ogni costruzione di espressioni regolari eseguirà la scansione delle 5000 voci per vedere se è stata precedentemente memorizzata nella cache. C'è unserraturaintorno al controllo, in modo che il controllo possa diminuire il parallelismo e introdurre il blocco del thread.

Il numero è impostato abbastanza basso per proteggerti da casi come questo, anche se in alcuni casi potresti non avere altra scelta che aumentarlo.

Mioforte raccomandazionesarebbemaipassa ilRegexOptions.Compiledopzione a un helper statico.

Per esempio:

\\ WARNING: bad code
Regex.IsMatch("10000", @"\\d+", RegexOptions.Compiled)

Il motivo è che stai molto rischiando di perdere la cache LRU che attiverà un filesuper costosocompilare. Inoltre, non hai idea di cosa stiano facendo le librerie da cui dipendi, quindi hai poca capacità di controllare o prevedere i filemigliore possibiledimensione della cache.

Guarda anche:Blog del team BCL


Nota: questo è rilevante per .NET 2.0 e .NET 4.0. Ci sono alcune modifiche previste nella 4.5 che potrebbero causare una revisione.

fonte
Translate

Questa voce nel blog del team di BCL offre una bella panoramica: "Prestazioni di espressione regolare".

In breve, ci sono tre tipi di regex (ciascuno più veloce del precedente):

  1. interpretato

    veloce da creare al volo, lento da eseguire

  2. compilato(quello di cui sembri chiedere)

    più lento da creare al volo, veloce da eseguire (buono per l'esecuzione in loop)

  3. precompilato

    crea in fase di compilazione della tua app (nessuna penalità per la creazione in fase di esecuzione), veloce da eseguire

Quindi, se intendi eseguire la regex solo una volta o in una sezione non critica per le prestazioni della tua app (ad es. Convalida dell'input dell'utente), stai bene con l'opzione 1.

Se intendi eseguire la regex in un ciclo (cioè l'analisi riga per riga del file), dovresti scegliere l'opzione 2.

Se hai molte espressioni regolari che non cambieranno mai per la tua app e vengono utilizzate intensamente, potresti scegliere l'opzione 3.

fonte
Translate

Va notato che le prestazioni delle espressioni regolari a partire da .NET 2.0 sono state migliorate con una cache MRU di espressioni regolari non compilate. Il codice della libreria Regex non reinterpreta più ogni volta la stessa espressione regolare non compilata.

Quindi c'è potenzialmente una performance maggiorepenacon un'espressione regolare compilata e al volo. Oltre a tempi di caricamento più lenti, il sistema utilizza anche più memoria per compilare l'espressione regolare in opcode.

In sostanza, il consiglio corrente è di non compilare un'espressione regolare o di compilarle in anticipo in un assembly separato.

Rif: BCL Team BlogPerformance Regular Expression [David Gutierrez]

fonte
Translate

Spero che il codice seguente ti aiuti a comprendere il concetto di funzioni di re.compile

import re

x="""101 COM    Computers
205 MAT   Mathematics
189 ENG   English
222 SCI Science
333 TA  Tamil
5555 KA  Kannada
6666  TL  Telugu
777777 FR French
"""

#compile reg expression / successfully compiled regex can be used in any regex 
#functions    
find_subject_code=re.compile("\d+",re.M)
#using compiled regex in regex function way - 1
out=find_subject_code.findall(x)
print(out)
#using compiled regex in regex function way - 2
out=re.findall(find_numbers,x)
print(out)

#few more eg:
#find subject name
find_subjectnames=re.compile("(\w+$)",re.M) 
out=find_subjectnames.findall(x)
print(out)


#find subject SHORT name
find_subject_short_names=re.compile("[A-Z]{2,3}",re.M) 
out=find_subject_short_names.findall(x)
print(out)
fonte