2011-02-08 14 views
6

सी # में स्ट्रिंग प्रत्यय की जांच करने का सबसे तेज़ तरीका क्या है?सी # (.NET 4.0) में फास्ट स्ट्रिंग प्रत्यय जांच?

मुझे किसी विशेष अवधि के लिए प्रत्येक स्ट्रिंग को बड़ी सूची में (5000 से 100000 आइटमों तक कहीं भी) जांचना होगा। इस शब्द को स्ट्रिंग के भीतर कभी भी एम्बेड करने की गारंटी नहीं है। दूसरे शब्दों में, यदि स्ट्रिंग में शब्द होता है, तो यह स्ट्रिंग के अंत में होगा। स्ट्रिंग को प्रत्यय से अधिक होने की भी गारंटी दी जाती है। सांस्कृतिक जानकारी महत्वपूर्ण नहीं है।

1. Substring Comparison - 13.60ms 
2. String.Contains - 22.33ms 
3. CompareInfo.IsSuffix - 24.60ms 
4. String.EndsWith - 29.08ms 
5. String.LastIndexOf - 30.68ms 

ये औसत समय है:

ये कैसे अलग अलग तरीकों 100000 तार (उनमें से आधे प्रत्यय है) के खिलाफ प्रदर्शन किया है। [संपादित करें] यह उल्लेख करने के लिए भूल गए कि स्ट्रिंग्स को अलग सूचियों में भी रखा जाता है, लेकिन यह महत्वपूर्ण नहीं है। हालांकि यह चलने वाले समय में जोड़ता है।

मेरी प्रणाली सबस्ट्रिंग तुलना (स्ट्रिंग के उपयोग को स्ट्रिंग के अंत में निकालने और स्ट्रिक्स शब्द की तुलना में स्ट्रिंग के अंत तक निकालने पर) 100000 तारों के खिलाफ परीक्षण करते समय लगातार सबसे तेज़ होता है। सबस्ट्रिंग तुलना का उपयोग करने में समस्या यह है कि कचरा संग्रह इसे काफी धीमा कर सकता है (अन्य तरीकों से अधिक) क्योंकि स्ट्रिंग। सब्स्ट्रिंग नए तार बनाता है। प्रभाव .NET 4.0 में उतना बुरा नहीं है जितना कि यह 3.5 और नीचे था, लेकिन यह अभी भी ध्यान देने योग्य है। मेरे परीक्षणों में, स्ट्रिंग.Substring 12000-13000 तारों के सेट पर लगातार धीमी प्रदर्शन किया। यह सिस्टम और कार्यान्वयन के बीच स्पष्ट रूप से भिन्न होगा।

[संपादित करें] बेंचमार्क कोड: http://pastebin.com/smEtYNYN

[संपादित करें] FlyingStreudel के कोड तेजी से चलाता है, लेकिन StringComparison.Ordinal साथ संयोजन के रूप में EndsWith का उपयोग करने का जॉन स्कीट की सिफारिश सबसे अच्छा विकल्प प्रतीत होता है।

+3

.NET क्या कर रहा है, इस पर कोई टिप्पणी नहीं है, लेकिन यदि आपको कई प्रत्यय के लिए एक ही स्ट्रिंग का परीक्षण करने की आवश्यकता है, तो प्रत्यय पेड़ का निर्माण करने की सलाह दी जा सकती है: http://en.wikipedia.org/wiki/Suffix_tree – mquander

उत्तर

18

यदि 100,000 तारों की जांच करने के लिए समय निकाला गया है, तो क्या इससे वाकई कोई फर्क पड़ता है?

व्यक्तिगत रूप से मैं string.EndsWith का उपयोग इस आधार पर करता हूं कि यह सबसे वर्णनात्मक है: यह वास्तव में कहता है कि आप परीक्षण करने का प्रयास कर रहे हैं।

मैं इस तथ्य के बारे में कुछ हद तक संदिग्ध हूं कि ऐसा लगता है कि यह सबसे खराब प्रदर्शन कर रहा है ... यदि आप अपना बेंचमार्क कोड पोस्ट कर सकते हैं, तो यह बहुत उपयोगी होगा। (विशेष रूप से, इसे वास्तव में string.Contains के रूप में उतना ही काम नहीं करना चाहिए।)

क्या आपने ऑर्डिनल मैच निर्दिष्ट करने का प्रयास किया है? यही कारण है कि अच्छी तरह से कर सकते हैं यह काफी तेजी:

if (x.EndsWith(y, StringComparison.Ordinal)) 
बेशक

, आपको नहीं करना चाहिए कि जब तक आप चाहते एक क्रमसूचक तुलना - आप सांस्कृतिक रूप से संवेदनशील मैचों उम्मीद कर रहे हैं? (डेवलपर्स इस तरह की चीज पर विचार नहीं करते हैं, और मैं बहुत दृढ़ता से उस श्रेणी में शामिल हूं।)

+0

इसमें केस प्रदर्शन एक मुद्दा है क्योंकि एप्लिकेशन का उपयोग कैसे किया जा रहा है (यह डेटा के कई सेट को संभालता है)। – ilitirit

+0

@ilitirit: तो आपकी लक्षित गति क्या है? कितनी तेज पर्याप्त तेज है?100,000 तारों के लिए 2 9 एमएमएस मेरे लिए बहुत तेज़ लगता है। आपके आवेदन को कितनी बार ऐसा करने की आवश्यकता है? –

+0

यह वास्तव में 2 9 मिमी से तेज है लेकिन तारों को अलग-अलग सूचियों में डाल दिया जाता है ताकि ऊपर की ओर बढ़ जाए। संस्कृति महत्वपूर्ण नहीं है क्योंकि तार अनिवार्य रूप से मशीन से उत्पन्न फाइलनाम हैं। मैं मानता हूं कि गति में अंतर 1 पुनरावृत्ति से अधिक नहीं है लेकिन यह एकाधिक सेटों पर संचालित होता है (मुझे यकीन नहीं है कि यह कितने - दिन/महीने के समय पर निर्भर करता है)। मुझे उन शक्तियों से पूछना होगा जो उनके प्रदर्शन मीट्रिक के बारे में हैं। – ilitirit

0

क्या आपने सीधे पहुंच का प्रयास किया था? मेरा मतलब है, आप समान स्ट्रिंग के लिए एक लूप देख सकते हैं, यह सबस्ट्रिंग बनाने और समान व्यवहार करने से तेज़ हो सकता है।

int i,j; 
foreach(String testing in lists){ 
    i=0; 
    j=0; 
    int ok=1; 
    while(ok){ 
     i = testing.lenght - PATTERN.lenght; 
     if(i>0 && i<testing.lenght && testing[i] != PATTERN[j]) 
     ok = 0; 
     i++; 
     j++; 
    } 
    if(ok) return testing; 
} 

इसके अलावा यदि यह बड़ा तार है, तो आप हैश का उपयोग करने का प्रयास कर सकते हैं।

+0

यह आमतौर पर एक बहुत बुरा विचार है। स्ट्रिंग खोज के लिए आपको हमेशा फ्रेमवर्क रूटीन का उपयोग करना चाहिए, या यूनिकोड विषमता (चरित्र संयोजन, आदि) के कारण आपको परेशानी का सामना करना पड़ता है। बेशक, 1) गति सटीकता से अधिक महत्वपूर्ण है, और 2) आप केवल अंग्रेजी पात्रों के साथ काम करेंगे। – erikkallen

+0

हैश का उपयोग करने का विचार उस मदद की संभावना नहीं है; उत्पन्न करने के लिए पूरे स्ट्रिंग को पढ़ने की आवश्यकता है। हैश की तुलना करने के लिए केवल इतना ही वर्ण पढ़ने की आवश्यकता है कि यह पता लगाने के लिए कोई मिलान नहीं है। हैश द्वारा – Brian

+0

, मेरा मतलब है सबस्ट्रिंग पर हैश। लेकिन सोचने के बाद यह स्पष्ट रूप से ऐसा करने का एक बुरा तरीका है। – ykatchou

4

मुझे पता नहीं है कि यह कितना तेज़ है, लेकिन यह वही है जो मैं करूँगा?

static bool HasSuffix(string check, string suffix) 
{ 
    int offset = check.Length - suffix.Length; 
    for (int i = 0; i < suffix.Length; i++) 
    { 
     if (check[offset + i] != suffix[i]) 
     { 
      return false; 
     } 
    } 
    return true; 
} 

संपादित करें: ओह x2

संपादित करें: तो मैं अपने खुद के छोटे से बेंचमार्क लिखा था ... इस गिनती करता है? यह एक लाख स्ट्रिंग का मूल्यांकन करने के 25 परीक्षण चलाता है और प्रदर्शन में अंतर का औसत लेता है। मैंने कितनी बार इसे चलाया, यह लगातार उत्पादन कर रहा था कि चार करोड़पति एक लाख से अधिक रिकॉर्ड ~ 10-40ms से तेज थे। तो यह दक्षता (.000000001/कॉल) में एक बेहद महत्वहीन वृद्धि है :) सभी में मुझे संदेह है कि इससे कोई फर्क नहीं पड़ता कि आप किस विधि को लागू करते हैं।

class Program 
{ 
    volatile static List<string> strings; 
    static double[] results = new double[25]; 
    static void Main(string[] args) 
    { 
     strings = new List<string>(); 
     Random r = new Random(); 
     for (int rep = 0; rep < 25; rep++) 
     { 
      Console.WriteLine("Run " + rep); 
      strings.Clear(); 
      for (int i = 0; i < 1000000; i++) 
      { 
       string temp = ""; 
       for (int j = 0; j < r.Next(3, 101); j++) 
       { 
        temp += Convert.ToChar(
         Convert.ToInt32(
         Math.Floor(26 * r.NextDouble() + 65))); 
       } 
       if (i % 4 == 0) 
       { 
        temp += "abc"; 
       } 
       strings.Add(temp); 
      } 
      OrdinalWorker ow = new OrdinalWorker(strings); 
      CharWorker cw = new CharWorker(strings); 
      if (rep % 2 == 0) 
      { 
       cw.Run(); 
       ow.Run(); 
      } 
      else 
      { 
       ow.Run(); 
       cw.Run();      
      } 
      Thread.Sleep(1000); 
      results[rep] = ow.finish.Subtract(cw.finish).Milliseconds; 
     } 
     double tDiff = 0; 
     for (int i = 0; i < 25; i++) 
     { 
      tDiff += results[i]; 
     } 
     double average = tDiff/25; 
     if (average < 0) 
     { 
      average = average * -1; 
      Console.WriteLine("Char compare faster by {0}ms average", 
       average.ToString().Substring(0, 4)); 
     } 
     else 
     { 
      Console.WriteLine("EndsWith faster by {0}ms average", 
       average.ToString().Substring(0, 4)); 
     } 

    } 
} 



class OrdinalWorker 
{ 
    List<string> list; 
    int count; 
    public Thread t; 
    public DateTime finish; 
    public OrdinalWorker(List<string> l) 
    { 
     list = l; 
    } 

    public void Run() 
    { 
     t = new Thread(() => { 
      string suffix = "abc"; 
      for (int i = 0; i < list.Count; i++) 
      { 
       count = (list[i].EndsWith(suffix, StringComparison.Ordinal)) ? 
        count + 1 : count; 
      } 
      finish = DateTime.Now; 
     }); 
     t.Start(); 
    } 
} 

class CharWorker 
{ 
    List<string> list; 
    int count; 
    public Thread t; 
    public DateTime finish; 
    public CharWorker(List<string> l) 
    { 
     list = l; 
    } 

    public void Run() 
    { 
     t = new Thread(() => 
     { 
      string suffix = "abc"; 
      for (int i = 0; i < list.Count; i++) 
      { 
       count = (HasSuffix(list[i], suffix)) ? count + 1 : count; 
      } 
      finish = DateTime.Now; 
     }); 
     t.Start(); 
    } 

    static bool HasSuffix(string check, string suffix) 
    { 
     int offset = check.Length - suffix.Length; 
     for (int i = 0; i < suffix.Length; i++) 
     { 
      if (check[offset + i] != suffix[i]) 
      { 
       return false; 
      } 
     } 
     return true; 
    } 
} 
+0

यह देखना दिलचस्प होगा कि क्या ऑप्टिमाइज़र दो स्ट्रिंग एक्सेसों पर आउट-ऑफ-बाउंड चेक को समाप्त करता है या नहीं। –

+1

मुझे आश्चर्य होगा कि 'एंड्सविथ' का अंतर्निहित कार्यान्वयन इस तरह के समान नहीं दिखता था, हालांकि मुझे संदेह है कि यह शायद 'बाहरी'-ed और/या 'असुरक्षित' है (और शायद संस्कृति-जागरूकता से थोड़ा सा हैमस्ट्रंग आदि भी)। – LukeH

+0

एर तो मुझे लगता है कि यह जांचने के लिए एक स्ट्रिंग मान रहा है प्रत्यय से कम कभी नहीं है? प्रत्यय की तुलना में कम स्ट्रिंग पर विचार करने की तरह अनावश्यक रूप से प्रत्यय कभी भी शामिल नहीं हो सकता है। – FlyingStreudel

13

जॉन बिल्कुल सही है; यह संभावित रूप से एक सेब-टू-सेब तुलना नहीं है क्योंकि अलग-अलग स्ट्रिंग विधियों में अपराधी संवेदनशीलता के लिए अलग-अलग डिफ़ॉल्ट होते हैं। सुनिश्चित करें कि आप तुलनात्मक अर्थशास्त्र प्राप्त कर रहे हैं जो आप प्रत्येक में करना चाहते हैं।

जॉन के उत्तर के अलावा, मैं जोड़ता हूं कि प्रासंगिक प्रश्न "सबसे तेज़ है?" बल्कि "जो बहुत धीमी है?" इस कोड के लिए आपका प्रदर्शन लक्ष्य क्या है? सबसे धीमी विधि अभी भी फिल्म प्रोजेक्टर को अगले फ्रेम तक अग्रिम करने के लिए कम समय में परिणाम पाती है, और स्पष्ट रूप से यह मनुष्यों द्वारा उल्लेखनीय नहीं है। यदि आपका लक्ष्य यह है कि खोज उपयोगकर्ता के लिए तात्कालिक दिखाई देती है तो आप कर चुके हैं; इनमें से कोई भी तरीका काम करता है। यदि आपका लक्ष्य यह है कि खोज मिलीसेकंड से कम लेती है तो उन तरीकों में से कोई भी काम नहीं करता है; वे परिमाण के सभी आदेश बहुत धीमी हैं। बजट क्या है?

+4

+1 "जो बहुत धीमा है" के लिए बजट विचारों पर विचारों को अच्छी तरह रीडायरेक्ट करता है। –

0

मैं इस क्षेत्र में एक विशेषज्ञ होने का दावा नहीं करता हूं, हालांकि मुझे कम से कम कुछ हद तक प्रोफाइल करने के लिए मजबूर होना पड़ा (पूरी तरह से जानना कि मेरा कल्पित परिदृश्य आपके से अलग होगा) और यहां मैं यही हूं के साथ आया था:

ऐसा लगता है, कम से कम मेरे लिए, EndsWithLastIndexOf लगातार दूसरे में आने के साथ ले लेता है, कुछ समय कर रहे हैं:

SubString: 00:00:00.0191877 
Contains:  00:00:00.0201980 
CompareInfo: 00:00:00.0255181 
EndsWith:  00:00:00.0120296 
LastIndexOf: 00:00:00.0133181 

ये 100,000 तार प्रसंस्करण जहां वांछित प्रत्यय दिखाई दिया से बटोरा गया में सभी स्ट्रिंग्स और इसलिए मेरे लिए बस जॉन के जवाब को आकर्षित करता है (जहां लाभ गति और वर्णनात्मकता दोनों है)। और उपयोग किया गया कोड इन परिणामों में आने के लिए:

class Program 
{ 
    class Profiler 
    { 
     private Stopwatch Stopwatch = new Stopwatch(); 

     public TimeSpan Elapsed { get { return Stopwatch.Elapsed; } } 

     public void Start() 
     { 
      Reset(); 
      Stopwatch.Start(); 
     } 

     public void Stop() 
     {    
      Stopwatch.Stop(); 
     } 

     public void Reset() 
     { 
      Stopwatch.Reset(); 
     } 
    } 

    static string suffix = "_sfx"; 
    static Profiler profiler = new Profiler(); 
    static List<string> input = new List<string>(); 
    static List<string> output = new List<string>(); 

    static void Main(string[] args) 
    { 
     GenerateSuffixedStrings(); 

     FindStringsWithSuffix_UsingSubString(input, suffix); 
     Console.WriteLine("SubString: {0}", profiler.Elapsed); 

     FindStringsWithSuffix_UsingContains(input, suffix); 
     Console.WriteLine("Contains:  {0}", profiler.Elapsed); 

     FindStringsWithSuffix_UsingCompareInfo(input, suffix); 
     Console.WriteLine("CompareInfo: {0}", profiler.Elapsed); 

     FindStringsWithSuffix_UsingEndsWith(input, suffix); 
     Console.WriteLine("EndsWith:  {0}", profiler.Elapsed); 

     FindStringsWithSuffix_UsingLastIndexOf(input, suffix); 
     Console.WriteLine("LastIndexOf: {0}", profiler.Elapsed); 

     Console.WriteLine(); 
     Console.WriteLine("Press any key to exit..."); 
     Console.ReadKey(); 
    } 

    static void GenerateSuffixedStrings() 
    { 
     for (var i = 0; i < 100000; i++) 
     { 
      input.Add(Guid.NewGuid().ToString() + suffix); 
     } 
    } 

    static void FindStringsWithSuffix_UsingSubString(IEnumerable<string> strings, string suffix) 
    { 
     output.Clear(); 
     profiler.Start(); 
     foreach (var s in strings) 
     { 
      if(s.Substring(s.Length - 4) == suffix) 
       output.Add(s); 
     } 
     profiler.Stop(); 
    } 

    static void FindStringsWithSuffix_UsingContains(IEnumerable<string> strings, string suffix) 
    { 
     output.Clear(); 
     profiler.Start(); 
     foreach (var s in strings) 
     { 
      if (s.Contains(suffix)) 
       output.Add(s); 
     } 
     profiler.Stop(); 
    } 

    static void FindStringsWithSuffix_UsingCompareInfo(IEnumerable<string> strings, string suffix) 
    { 
     var ci = CompareInfo.GetCompareInfo("en-GB"); 
     output.Clear(); 
     profiler.Start(); 
     foreach (var s in strings) 
     { 
      if (ci.IsSuffix(s, suffix)) 
       output.Add(s); 
     } 
     profiler.Stop(); 
    } 

    static void FindStringsWithSuffix_UsingEndsWith(IEnumerable<string> strings, string suffix) 
    { 
     output.Clear(); 
     profiler.Start(); 
     foreach (var s in strings) 
     { 
      if (s.EndsWith(suffix)) 
       output.Add(s); 
     } 
     profiler.Stop(); 
    } 

    static void FindStringsWithSuffix_UsingLastIndexOf(IEnumerable<string> strings, string suffix) 
    { 
     output.Clear(); 
     profiler.Start(); 
     foreach (var s in strings) 
     { 
      if (s.LastIndexOf(suffix) == s.Length - 4) 
       output.Add(s); 
     } 
     profiler.Stop(); 
    } 
} 

संपादित करें:

के रूप में टिप्पणी की, मैं केवल के साथ फिर से इस कोशिश की एक प्रत्यय आवेदन किया है और हो रही तार से कुछ इन परिणामों हैं:

SubString: 00:00:00.0079731 
Contains:  00:00:00.0243696 
CompareInfo: 00:00:00.0334056 
EndsWith:  00:00:00.0196668 
LastIndexOf: 00:00:00.0229599 

स्ट्रिंग जनरेटर विधि इस प्रकार है अद्यतन किया गया था, तार उत्पादन करने के लिए:

static void GenerateSuffixedStrings() 
    { 
     var nxt = false; 
     var rnd = new Random();    
     for (var i = 0; i < 100000; i++) 
     { 
      input.Add(Guid.NewGuid().ToString() + 
       (rnd.Next(0, 2) == 0 ? suffix : string.Empty)); 
     } 
    } 

इसके अलावा, इस प्रवृत्ति को जारी रहा तो स्ट्रिंग में से कोई भी एक प्रत्यय है:

SubString: 00:00:00.0055584 
Contains:  00:00:00.0187089 
CompareInfo: 00:00:00.0228983 
EndsWith:  00:00:00.0114227 
LastIndexOf: 00:00:00.0199328 

हालांकि, इस अंतर को फिर से छोटा कर देता आदानों की एक चौथाई एक प्रत्यय (पहली तिमाही बताए, तो randomise को छँटाई कवरेज):

SubString: 00:00:00.0302997 
Contains:  00:00:00.0305685 
CompareInfo: 00:00:00.0306335 
EndsWith:  00:00:00.0351229 
LastIndexOf: 00:00:00.0322899 

निष्कर्ष?आईएमओ, और जॉन के साथ सहमत, EndsWith जाने का रास्ता लगता है (वैसे भी इस सीमित परीक्षण के आधार पर)।

आगे संपादित करें:

जॉन की जिज्ञासा का इलाज करने के लिए मैं के साथ या बिना Ordinal स्ट्रिंग तुलना, EndsWith पर कुछ और परीक्षण भाग गया ...

उनमें से एक चौथाई के साथ 100,000 तार पर प्रत्यय:

EndsWith:   00:00:00.0795617 
OrdinalEndsWith:  00:00:00.0240631 

उनमें से एक चौथाई प्रत्यय के साथ 1,000,000 तार पर:

EndsWith:   00:00:00.5460591 
OrdinalEndsWith:  00:00:00.2807860 

उनमें से एक चौथाई प्रत्यय के साथ 10,000,000 तार पर:

EndsWith:   00:00:07.5889581 
OrdinalEndsWith:  00:00:03.3248628 

ध्यान दें कि मैं केवल अंतिम परीक्षण एक बार तार पैदा करने के रूप में साबित कर दिया इस लैपटॉप एक प्रतिस्थापन की जरूरत है भाग गया

+0

आपके सभी तारों में प्रत्यय है। जब मैं कोड को केवल आधे प्रत्यय देने के लिए बदलता हूं तो मुझे अपने घर की मशीन पर जो भी पोस्ट किया गया है, उसके समान परिणाम मिलते हैं (अभी काम पर नहीं)। – ilitirit

+0

सही, जैसा कि मैंने समझाया। मैंने फिर से इसे 'यादृच्छिक रूप से' लागू प्रत्यय के साथ फिर से प्रयास किया और बहुत ही समान निष्कर्ष निकाले - जैसा कि आपने उल्लेख किया है, सबसे बड़ा अंतर, 'सबस्ट्रिंग' लगातार शीर्ष पर आ रहा है। हालांकि, मुझे अभी भी अन्य कारणों से जॉन से सहमत होना पड़ेगा, भले ही गति न हो। –

+0

यदि आप 's.EndsWith (प्रत्यय, स्ट्रिंग कॉम्परिसन। ऑर्डिनल) का उपयोग करते हैं तो क्या होता है? –

6

मैंने आपके बेंचमार्क कोड को देखा और स्पष्ट रूप से, यह डोडी दिखता है।

आप सभी प्रकार की अपर्याप्त चीज़ों को माप रहे हैं जिनके साथ आप मापना चाहते हैं; आप फोरैच की लागत को माप रहे हैं और एक सूची में जोड़ रहे हैं, जिनमें से दोनों को उस परिमाण के समान क्रम की लागत हो सकती है, जिसकी आप जांच कर रहे हैं।

इसके अलावा, आप पहले रन को नहीं फेंक रहे हैं; याद रखें, जेआईटी कंपाइलर उस कोड को जिट करने जा रहा है जिसे आप लूप के माध्यम से पहले समय कहते हैं, और यह दूसरा समय जाने के लिए गर्म और तैयार होने जा रहा है, इसलिए आपके परिणाम इसलिए खराब हो जाएंगे; आप कई छोटी चीजों के साथ एक संभावित रूप से बहुत बड़ी चीज औसत कर रहे हैं। अतीत में जब मैंने ऐसा किया है तो मैंने उन स्थितियों की खोज की है जहां जिट समय वास्तव में बाकी सब कुछ के समय पर हावी था। क्या वह यथार्थवादी है? क्या आप जित समय को मापने का मतलब रखते हैं, या इसे औसत के हिस्से के रूप में नहीं माना जाना चाहिए?

-1

यहां बहुत अच्छी जानकारी है। मैं यह ध्यान रखना चाहता था कि यदि आपका प्रत्यय छोटा है, तो यह पिछले कुछ पात्रों को व्यक्तिगत रूप से देखने के लिए और भी तेज़ हो सकता है। प्रश्न में बेंचमार्क कोड का मेरा संशोधित संस्करण यहां है: http://pastebin.com/6nNdbEvW। यह शोध करे परिणाम देता है:

  1. अंतिम चार समानता: 1.52 एमएस (50000)
  2. अंतिम 2 चार समानता: 1.56 एमएस (50000)
  3. EndsWith StringComparison.Ordinal का उपयोग कर: 3.75 एमएस (50000)
  4. शामिल: 11.10 एमएस (50000)
  5. LastIndexOf: 14.85 एमएस (50000)
  6. IsSuffix: 11.30 एमएस (50000)
  7. सबस्ट्रिंग तुलना: 17.69 एमएस (50000)