C# में रेगुलर एक्सप्रेशन में \\d, [0-9] से धीमा क्यों है? विश्लेषण

शुरुआती लोगों के लिए ऑनलाइन Python प्रशिक्षक

Python आसानी से सीखें बिना सिद्धांत के बोझ के। स्वचालित जांच के साथ व्यावहारिक कार्य हल करें और ब्राउज़र में सीधे कोड लिखें।

पाठ्यक्रम शुरू करें

परिचय: रेगुलर एक्सप्रेशन की गति का मिथक

\n\n

यदि आपने कभी C# में कोड लिखा है और रेगुलर एक्सप्रेशन का उपयोग किया है, तो आप निश्चित रूप से इस दुविधा का सामना किया होगा: अंक खोजने के लिए \\d या [0-9] का उपयोग करें? पहली नज़र में कोई अंतर नहीं है - दोनों अंक ढूंढते हैं। लेकिन अनुभवी डेवलपर्स, जो प्रदर्शन को चमकाने तक पॉलिश करते हैं, जानते हैं: \\d, [0-9] की तुलना में काफी धीमा हो सकता है।

\n\n

Stack Overflow पर इस प्रश्न ने हजारों व्यूज़ और दर्जनों टिप्पणियाँ प्राप्त की हैं। आज हम केवल उत्तर को दोबारा नहीं बताएंगे, बल्कि .NET Regex इंजन के अंदर झांकेंगे और समझेंगे कि ऐसा क्यों होता है और यह आपके कोड को कैसे प्रभावित करता है। हम C# में वास्तविक बेंचमार्क लिखेंगे, IL-कोड देखेंगे और जानेंगे कि कब अलार्म बजाना चाहिए और कब आराम कर सकते हैं।

\n\n

तैयार हैं? चलिए शुरू करते हैं!

\n\n

C# में \\d और [0-9] क्या हैं?

\n\n

शुरुआत के लिए - बुनियादी बातें। अधिकांश रेगुलर एक्सप्रेशन कार्यान्वयन (C# सहित) में, \\d "कोई भी अंक" के लिए एक शॉर्टकट है। और [0-9] एक कैरेक्टर क्लास है जो स्पष्ट रूप से '0' से '9' तक की रेंज को सूचीबद्ध करता है।

\n\n

लगता है कि यह एक ही बात है। लेकिन शैतान, हमेशा की तरह, विवरण में है।

\n\n

Unicode बनाम ASCII

\n\n

.NET में (.NET Framework 4.5 और उसके बाद से), \\d डिफ़ॉल्ट रूप से Unicode श्रेणी Nd (Number, Decimal Digit) के किसी भी कैरेक्टर से मेल खाता है। इसमें न केवल अरबी अंक 0-9 शामिल हैं, बल्कि:

\n
    \n
  • देवनागरी अंक (०१२३...)
  • \n
  • तमिल लिपि के अंक
  • \n
  • पूर्वी-अरबी अंक
  • \n
  • और दर्जनों अन्य संख्या प्रणालियाँ
  • \n
\n\n

जबकि [0-9] ASCII कोड (48-57) की एक कठोर रूप से परिभाषित रेंज है। केवल 0,1,2,3,4,5,6,7,8,9। कोई Unicode नहीं।

\n\n

गति अलग क्यों है? अंदरूनी विश्लेषण

\n\n

अब मुख्य बात: \\d धीमा क्यों है? आइए इंजन स्तर पर समझते हैं।

\n\n

1. जाँचों की संख्या

\n\n

जब इंजन [0-9] का सामना करता है, तो वह एक काम करता है: जाँचता है कि कैरेक्टर का कोड 48-57 की रेंज में है या नहीं। यह एक तुलना है (या दो, यदि सीमाओं पर विचार करें)।

\n\n

जब इंजन \\d का सामना करता है, तो उसे:

\n
    \n
  1. कैरेक्टर की Unicode श्रेणी प्राप्त करनी होती है (यह पहले से ही केवल int से अधिक महंगा है)।
  2. \n
  3. इसकी तुलना Nd श्रेणी से करनी होती है।
  4. \n
\n\n

Unicode श्रेणी केवल एक संख्या नहीं है, यह एक पूरा फ्लैग है जो आंतरिक तालिकाओं के माध्यम से गणना की जाती है। लैटिन अंकों के लिए परिणाम समान होगा, लेकिन उस तक पहुँचने का रास्ता लंबा है।

\n\n

2. कैशिंग और JIT

\n\n

.NET में, Regex को इंटरमीडिएट कोड (IL) या यहाँ तक कि नेटिव कोड (यदि RegexOptions.Compiled का उपयोग किया जाता है) में संकलित किया जाता है। [0-9] के लिए, JIT निर्देशों का एक बहुत ही सरल अनुक्रम उत्पन्न कर सकता है: कुछ तुलनाएँ और एक सशर्त जंप। \\d के लिए - CharUnicodeInfo.GetUnicodeCategory विधि का कॉल और उसके बाद तुलना।

\n\n

विधि का कॉल ओवरहेड है: आर्गुमेंट्स को push करना, call, स्टैक की जाँच। भले ही विधि छोटी हो, फिर भी यह इनलाइन तुलना से धीमी है।

\n\n

3. वास्तविक बेंचमार्क का उदाहरण

\n\n

आइए BenchmarkDotNet का उपयोग करके C# में एक सरल परीक्षण लिखें (यदि आप इसे दोहराना चाहते हैं तो इसे NuGet के माध्यम से इंस्टॉल करना सुनिश्चित करें):

\n\n
using BenchmarkDotNet.Attributes;\nusing BenchmarkDotNet.Running;\nusing System.Text.RegularExpressions;\n\npublic class RegexBenchmark\n{\n    private static readonly Regex _digitPattern = new Regex(@"\\d", RegexOptions.Compiled);\n    private static readonly Regex _rangePattern = new Regex(@"[0-9]", RegexOptions.Compiled);\n    \n    private const string TestString = "Hello123World456!";\n\n    [Benchmark]\n    public bool DigitPattern() => _digitPattern.IsMatch(TestString);\n\n    [Benchmark]\n    public bool RangePattern() => _rangePattern.IsMatch(TestString);\n}\n\nclass Program\n{\n    static void Main() => BenchmarkRunner.Run<RegexBenchmark>();\n}
\n\n

परिणाम (.NET पर

ब्लॉग

पुस्तक अनुशंसाएँ