RegexOptions.Compiled nasıl çalışır?


169

Düzenli bir ifadeyi derlenecek olarak işaretlediğinizde perde arkasında neler oluyor? Bu, önbelleğe alınmış normal bir ifadeyle nasıl karşılaştırılır / farklıdır?

Bu bilgileri kullanarak, hesaplama artışının performans artışıyla karşılaştırıldığında ne zaman ihmal edilebilir olduğunu nasıl belirlersiniz?


Regex en iyi uygulamaları için iyi bir kaynak: docs.microsoft.com/en-us/dotnet/standard/base-types/…
CAD bloke

Yanıtlar:


302

RegexOptions.Compilednormal ifade motoruna normal ifade ifadesini hafif kod oluşturma ( LCG ) kullanarak IL'ye derleme talimatı verir . Bu derleme nesnenin yapımı sırasında olur ve ağır da yavaşlatır. Buna karşılık, normal ifadeyi kullanan eşleşmeler daha hızlıdır.

Bu bayrağı belirtmezseniz, normal ifadeniz "yorumlanır" olarak kabul edilir.

Bu örneği ele alalım:

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 = @"^(([^<>()[\]\\.,;:\s@""]+"
      + @"(\.[^<>()[\]\\.,;:\s@""]+)*)|("".+""))@"
      + @"((\[[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[] { "sam@sam.com", "sss@s", "sjg@ddd.com.au.au", "onelongemail@oneverylongemail.com" };

    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]);
        });

    }
}

3 farklı düzenli ifade üzerinde 4 test gerçekleştirir. Önce tek bir testi maç kapalı bir kere (derlenmiş olmayan vs derlenmiş). İkinci olarak, aynı normal ifadeyi yeniden kullanan tekrarlanan eşleşmeleri test eder.

Makinemdeki sonuçlar (sürümde derlenmiş, hata ayıklayıcı eklenmemiş)

1000 tekli maç (Regex, Match and dispose)

Türü | Platformu | Önemsiz Sayı | Basit E-posta Kontrolü | Dahili E-posta Kontrolü
-------------------------------------------------- ----------------------------
Yorumlandı | x86 | 4 ms | 26 ms | 31 ms
Yorumlandı | x64 | 5 ms | 29 ms | 35 ms
Derlendi | x86 | 913 ms | 3775 ms | 4487 ms
Derlendi | x64 | 3300 ms | 21985 ms | 22793 ms

1.000.000 eşleşme - Regex nesnesini yeniden kullanma

Türü | Platformu | Önemsiz Sayı | Basit E-posta Kontrolü | Dahili E-posta Kontrolü
-------------------------------------------------- ----------------------------
Yorumlandı | x86 | 422 ms | 461 ms | 2122 ms
Yorumlandı | x64 | 436 ms | 463 ms | 2167 ms
Derlendi | x86 | 279 ms | 166 ms | 1268 ms
Derlendi | x64 | 281 ms | 176 ms | 1180 ms

Bu sonuçlar, düzenli düzenli ifadelerin nesneyi yeniden kullandığınız durumlarda % 60'a kadar daha hızlı olabileceğini göstermektedir Regex. Ancak bazı durumlarda 3'den fazla büyüklükte inşa edilebilir.

Ayrıca , normal ifadelerin derlenmesi söz konusu olduğunda , .NET'in x64 sürümünün 5 ila 6 kat daha yavaş olabileceğini gösterir .


Öneri olacaktır derlenmiş sürümünü kullanmak durumlarda nerede ya

  1. Nesne başlatma maliyetini umursamazsınız ve ekstra performans artışına ihtiyacınız vardır. (burada bir milisaniyelik kesirlerden bahsettiğimizi unutmayın)
  2. Başlatma maliyetine biraz önem veriyorsunuz, ancak Regex nesnesini uygulama ömrünüz boyunca telafi edecek kadar çok tekrar kullanıyorsunuz.

Eserler anahtar, Regex önbellek

Normal ifade motoru, Regexsınıftaki statik yöntemler kullanılarak test edilen son 15 normal ifadeyi içeren bir LRU önbelleği içerir .

Örneğin: Regex.Replace, Regex.Matchvb .. tüm kullanım Regex önbelleği.

Önbellek boyutu ayarlanarak artırılabilir Regex.CacheSize. Uygulamanızın yaşam döngüsü boyunca herhangi bir zamanda boyut değişikliklerini kabul eder.

Yeni normal ifadeler yalnızca Regex sınıfındaki statik yardımcılar tarafından önbelleğe alınır . Nesnelerinizi oluşturursanız, önbellek kontrol edilir (yeniden kullanım ve çarpma için), oluşturduğunuz normal ifade önbelleğe eklenmez .

Bu önbellek önemsiz bir LRU önbelleğidir, basit bir çift bağlantılı liste kullanılarak uygulanır. Bunu 5000'e yükseltirseniz ve statik yardımcılarda 5000 farklı çağrı kullanırsanız, her normal ifade yapısı daha önce önbelleğe alınmış olup olmadığını görmek için 5000 girişi tarar. Kontrolün etrafında bir kilit vardır , bu nedenle kontrol paralelliği azaltabilir ve iplik blokajı sağlayabilir.

Kendinizi bu gibi durumlardan korumak için sayı oldukça düşüktür, ancak bazı durumlarda artırmak için başka seçeneğiniz olmayabilir.

Benim güçlü öneri olacağını asla geçmesi RegexOptions.Compiledstatik yardımcı seçeneği.

Örneğin:

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

Bunun nedeni, LRU önbelleğinde süper pahalı bir derlemeyi tetikleyecek bir özlüyor olma riskini yüksek olmanızdır . Ayrıca, bağımlı olduğunuz kitaplıkların ne yaptığına dair hiçbir fikriniz yoktur, bu nedenle önbelleğin mümkün olan en iyi boyutunu denetleme veya tahmin etme konusunda çok az yeteneğiniz vardır .

Ayrıca bakınız: BCL takım blogu


Not : Bu, .NET 2.0 ve .NET 4.0 ile ilgilidir. 4.5'te bunun revize edilmesine neden olabilecek bazı değişiklikler var.


11
Mükemmel cevap. Kendi amaçlarım için Compiled, genellikle statik (uygulama çapında) bir Regexnesneyi sakladığım web sitesi kodunda kullanıyorum . Bu nedenle, RegexIIS uygulamayı başlattığında yalnızca bir kez oluşturulmalı ve ardından binlerce kez yeniden kullanılmalıdır. Uygulama sık sık yeniden başlatılmadığı sürece bu işe yarar.
Steve Wortham

W00! Bu bilgiler, işlemimi 8-13 saatten ~ 30 dakikaya çıkarmama yardımcı oldu. Teşekkür ederim!
Robert Christ

3
Harika cevap Sam, sürüm 4.5'te neler değiştiğini güncelleyebilir misiniz? (Yığını bir süre önce değiştirdiğini biliyorum ...)
gdoron Monica

@gdoronissupportingMonica NET 5.0'da bazı Regex performans iyileştirmeleri yapıldı. Bunun için bir blog yazısı gördüm. Buradan
kapozade

42

BCL Takım Blogu'ndaki bu giriş hoş bir genel bakış sunar: " Normal İfade performansı ".

Kısacası, üç tür normal ifade vardır (her biri öncekinden daha hızlı çalışır):

  1. yorumlanır

    anında oluşturmak hızlı, yürütmek yavaş

  2. derlenmiş (sormak istediğiniz gibi)

    anında oluşturmak için daha yavaş, yürütmek için hızlı (döngülerde yürütme için iyi)

  3. pre-derlenmiş

    uygulamanızın derlenme zamanında oluştur (çalışma zamanı oluşturma cezası yok), hızlı yürütme

Bu nedenle, normal ifadeyi yalnızca bir kez veya uygulamanızın performans açısından kritik olmayan bir bölümünde (yani kullanıcı girişi doğrulama) yürütmek istiyorsanız, seçenek 1'de sorun yoktur.

Normal ifadeyi bir döngüde çalıştırmak istiyorsanız (örneğin, dosyanın satır satır ayrıştırılması), seçenek 2 ile devam etmelisiniz.

Uygulamanız için hiçbir zaman değişmeyecek ve yoğun bir şekilde kullanılan birçok normal ifadeniz varsa, seçenek 3 ile gidebilirsiniz.


1
3 numara özel bir roslyn ile kolaylaştırılabilirCompileModule . Kahretsin, yeni platforma daha derinlemesine bakmam gerekiyor.
Christian Gollhardt

9

.NET 2.0'ın derlenmemiş düzenli ifadelerin bir MRU önbelleği ile düzenli ifadelerin performansının iyileştirildiği belirtilmelidir. Regex kütüphane kodu artık her derlendiğinde aynı derlenmemiş normal ifadeyi yeniden yorumlamaz.

Yani potansiyel olarak daha büyük bir performans cezası var derlenmiş ve anında düzenli ifade ile vardır. Daha yavaş yükleme sürelerine ek olarak, sistem normal ifadeyi opcod'lara derlemek için daha fazla bellek kullanır.

Esasen, mevcut tavsiye ya düzenli bir ifade derlemez ya da bunları ayrı bir toplantıya önceden derlemez.

Ref: BCL Takım Blogu Düzenli İfade performansı [David Gutierrez]



0

Umarım aşağıdaki kod yeniden derleme fonksiyonları kavramını anlamanıza yardımcı olur

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)

Cevabınız için teşekkürler, ancak kodunuz Python dilinde. Soru Microsoft .NET framework RegexOptions.Compiled seçeneğiyle ilgiliydi . Sorunun altında [ .net ] etiketini görebilirsiniz .
stomi

ohh evet! Teşekkürler stomy
Daniel Muthupandi
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.