RegexOptions.Compiledलाइट कोड पीढ़ी ( LCG ) का उपयोग करके नियमित अभिव्यक्ति अभिव्यक्ति को IL में संकलित करने के लिए नियमित अभिव्यक्ति इंजन को निर्देश देता है । इस संकलन वस्तु के निर्माण के दौरान होता है और भारी यह धीमा। बदले में, नियमित अभिव्यक्ति का उपयोग करने वाले मैच तेज होते हैं।
यदि आप इस ध्वज को निर्दिष्ट नहीं करते हैं, तो आपकी नियमित अभिव्यक्ति को "व्याख्या" माना जाता है।
इस उदाहरण को लें:
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 अलग-अलग नियमित अभिव्यक्तियों पर 4 परीक्षण करता है। पहले यह एक बार ऑफ मैच (संकलित बनाम गैर संकलित) का एकल परीक्षण करता है । दूसरा यह दोहराए गए मैचों का परीक्षण करता है जो समान नियमित अभिव्यक्ति का पुन: उपयोग करते हैं।
मेरी मशीन पर परिणाम (जारी में संकलित, कोई डिबगर संलग्न नहीं)
1000 एकल मैच (रेगेक्स का निर्माण, मिलान और निपटान)
प्रकार | प्लेटफार्म | तुच्छ संख्या | सरल ईमेल जाँच | ईमेल की जाँच करें
-------------------------------------------------- ----------------------------
व्याख्या की गई | x86 | 4 एमएस | 26 एमएस | ३१ मि
व्याख्या की गई | x64 | 5 एमएस | 29 एमएस | 35 एमएस
संकलित | x86 | 913 एमएस | 3775 एमएस | 4487 मि
संकलित | x64 | 3300 एमएस | 21985 एमएस | 22793 मि
1,000,000 मैच - रेगेक्स ऑब्जेक्ट का पुन: उपयोग
प्रकार | प्लेटफार्म | तुच्छ संख्या | सरल ईमेल जाँच | ईमेल की जाँच करें
-------------------------------------------------- ----------------------------
व्याख्या की गई | x86 | 422 एमएस | 461 एमएस | 2122 मि
व्याख्या की गई | x64 | 436 एमएस | 463 एमएस | 2167 मि
संकलित | x86 | 279 एमएस | 166 एमएस | 1268 मि
संकलित | x64 | 281 एमएस | 176 एमएस | 1180 मि
ये परिणाम बताते हैं कि संकलित नियमित अभिव्यक्तियाँ उन मामलों के लिए 60% तक तेज हो सकती हैं जहां आप Regexऑब्जेक्ट का पुन: उपयोग करते हैं । हालांकि कुछ मामलों में निर्माण के लिए परिमाण धीमे के 3 से अधिक आदेश हो सकते हैं ।
यह भी दर्शाता है कि .NET का x64 संस्करण नियमित अभिव्यक्ति के संकलन की बात आने पर 5 से 6 गुना धीमा हो सकता है।
सिफारिश उन मामलों में संकलित संस्करण का उपयोग करने के लिए होगी जहां या तो
- आपको ऑब्जेक्ट आरंभीकरण लागत के बारे में परवाह नहीं है और अतिरिक्त प्रदर्शन को बढ़ावा देने की आवश्यकता है। (ध्यान दें कि हम यहाँ एक मिलीसेकंड के अंशों की बात कर रहे हैं)
- आप आरंभीकरण लागत के बारे में थोड़ा ध्यान रखते हैं, लेकिन रेगेक्स ऑब्जेक्ट को इतनी बार पुन: उपयोग कर रहे हैं कि यह आपके आवेदन जीवन चक्र के दौरान इसके लिए क्षतिपूर्ति करेगा।
कार्यों में स्पैनर, रेगेक्स कैश
नियमित अभिव्यक्ति इंजन में एक LRU कैश होता है जो पिछले 15 नियमित अभिव्यक्तियों को रखता है जिन्हें Regexकक्षा पर स्थिर तरीकों का उपयोग करके परीक्षण किया गया था ।
उदाहरण के लिए: Regex.Replace, Regex.Matchआदि .. सभी उपयोग Regex कैश।
सेटिंग के द्वारा कैशे का आकार बढ़ाया जा सकता है Regex.CacheSize। यह आपके आवेदन के जीवन चक्र के दौरान किसी भी समय आकार में परिवर्तन को स्वीकार करता है।
नई नियमित अभिव्यक्तियाँ केवल रेगेक्स वर्ग पर स्थैतिक सहायकों द्वारा रखी गई हैं । यदि आप अपनी वस्तुओं का निर्माण करते हैं तो कैश की जांच की जाती है (पुन: उपयोग और टक्कर के लिए), हालांकि, आपके द्वारा निर्मित नियमित अभिव्यक्ति कैश में संलग्न नहीं होती है ।
यह कैश एक तुच्छ LRU कैश है, यह एक साधारण डबल लिंक्ड सूची का उपयोग करके कार्यान्वित किया जाता है। यदि आप इसे 5000 तक बढ़ाने के लिए होते हैं, और स्थिर सहायकों पर 5000 विभिन्न कॉल का उपयोग करते हैं, तो हर नियमित अभिव्यक्ति निर्माण यह देखने के लिए 5000 प्रविष्टियों को क्रॉल करेगा कि क्या यह पहले से कैश किया गया है। चेक के चारों ओर एक लॉक होता है, इसलिए चेक समानांतरता को कम कर सकता है और थ्रेड ब्लॉकिंग की शुरुआत कर सकता है।
इस तरह के मामलों से खुद को बचाने के लिए संख्या काफी कम है, हालांकि कुछ मामलों में आपके पास इसे बढ़ाने के अलावा कोई विकल्प नहीं हो सकता है।
मेरे मजबूत सिफारिश की जाएगी कभी नहीं पारित RegexOptions.Compiledएक स्थिर सहायक के लिए विकल्प।
उदाहरण के लिए:
\\ WARNING: bad code
Regex.IsMatch("10000", @"\\d+", RegexOptions.Compiled)
इसका कारण यह है कि आप LRU कैश पर एक बड़ी चूक कर रहे हैं जो एक सुपर महंगी संकलन को ट्रिगर करेगा । इसके अतिरिक्त, आपके पास यह पता नहीं है कि आपके द्वारा निर्भर पुस्तकालय क्या कर रहे हैं, इसलिए कैश के सर्वोत्तम संभावित आकार को नियंत्रित करने या भविष्यवाणी करने की बहुत कम क्षमता है ।
इसे भी देखें: BCL टीम ब्लॉग
नोट : यह .NET 2.0 और .NET 4.0 के लिए प्रासंगिक है। 4.5 में कुछ अपेक्षित बदलाव हैं जो इसे संशोधित कर सकते हैं।