2012-04-28 22 views
18

ILSpy दिखाता है कि String.IsNullOrEmptyString.Length के संदर्भ में लागू किया गया है। लेकिन फिर String.IsNullOrEmpty(s)s.Length == 0 से तेज़ क्यों है? ।।String.IsNullOrEmpty स्ट्रिंग की तुलना में तेज़ क्यों है। लम्बाई?

var stopwatches = Enumerable.Range(0, 4).Select(_ => new Stopwatch()).ToArray(); 
var strings = "A,B,,C,DE,F,,G,H,,,,I,J,,K,L,MN,OP,Q,R,STU,V,W,X,Y,Z,".Split(','); 
var testers = new Func<string, bool>[] { s => s == String.Empty, s => s.Length == 0, s => String.IsNullOrEmpty(s), s => s == "" }; 
int count = 0; 
for (int i = 0; i < 10000; ++i) { 
    stopwatches[i % 4].Start(); 
    for (int j = 0; j < 1000; ++j) 
     count += strings.Count(testers[i % 4]); 
    stopwatches[i % 4].Stop(); 
} 

(अन्य मानक के रूप में एक अलग रूप में, इसी तरह के परिणाम यह एक अपने कंप्यूटर पर चल रहे cruft के प्रभाव को कम से कम दिखाने के अलावा, परीक्षण की तुलना:

उदाहरण के लिए, यह 5% इस बेंचमार्क में तेज है करने के लिए रिक्त स्ट्रिंग IsNullOrEmpty की तुलना में धीमी 13% के बारे में एक ही बाहर आया था।)

साथ ही, क्यों IsNullOrEmpty केवल तेजी से 86 पर है, जबकि 64 पर String.Length लगभग 9% तेज है?

अद्यतन: टेस्ट सेटअप विवरण: 64-बिट विंडोज 7 पर चल रहे .NET 4.0, इंटेल कोर i5 प्रोसेसर, कंसोल प्रोजेक्ट "अनुकूलित कोड" सक्षम के साथ संकलित। हालांकि, "मॉड्यूल लोड पर जेआईटी अनुकूलन को दबाएं" भी सक्षम किया गया था (स्वीकृत उत्तर और टिप्पणियां देखें)।

अनुकूलन के साथ पूरी तरह से सक्षम, Length, के बारे में 14% प्रतिनिधि और अन्य भूमि के ऊपर से हटाया साथ IsNullOrEmpty की तुलना में तेजी है इस परीक्षण में के रूप में:

var strings = "A,B,,C,DE,F,,G,H,,,,I,J,,K,L,MN,OP,Q,R,,STU,V,,W,,X,,,Y,,Z,".Split(','); 
int count = 0; 
for (uint i = 0; i < 100000000; ++i) 
    count += strings[i % 32].Length == 0 ? 1 : 0; // Replace Length test with String.IsNullOrEmpty 
+7

आप जिस सटीक परिस्थिति में हैं, उसे जानने के बिना, मुझे पूरा भरोसा है कि ज्यादातर परिस्थितियों में यह जांचने के लिए सबसे तेज़ तरीका है कि स्ट्रिंग में कोई डेटा नहीं है या नहीं। – Andrew

+0

@minitech क्योंकि 4 परीक्षक हैं, और वह उन्हें स्वतंत्र रूप से समय दे रहा है। –

+0

'खाली' और '==' 'लंबाई' और' IsNullOrEmpty' के साथ तुलना कैसे करें? –

उत्तर

20

ऐसा इसलिए है क्योंकि आपने विजुअल स्टूडियो के भीतर से अपना बेंचमार्क चलाया जो कोड को अनुकूलित करने से जेआईटी कंपाइलर को रोकता है। अनुकूलन के बिना, इस कोड के लिए उत्पादन किया जाता है String.IsNullOrEmpty

00000000 push  ebp 
00000001 mov   ebp,esp 
00000003 sub   esp,8 
00000006 mov   dword ptr [ebp-8],ecx 
00000009 cmp   dword ptr ds:[00153144h],0 
00000010 je   00000017 
00000012 call  64D85BDF 
00000017 mov   ecx,dword ptr [ebp-8] 
0000001a call  63EF7C0C 
0000001f mov   dword ptr [ebp-4],eax 
00000022 movzx  eax,byte ptr [ebp-4] 
00000026 mov   esp,ebp 
00000028 pop   ebp 
00000029 ret 

और अब यह लंबाई == के लिए उत्पादन कोड से तुलना 0

00000000 push ebp 
00000001 mov ebp,esp 
00000003 sub esp,8 
00000006 mov dword ptr [ebp-8],ecx 
00000009 cmp dword ptr ds:[001E3144h],0 
00000010 je  00000017 
00000012 call 64C95BDF 
00000017 mov ecx,dword ptr [ebp-8] 
0000001a cmp dword ptr [ecx],ecx 
0000001c call 64EAA65B 
00000021 mov dword ptr [ebp-4],eax 
00000024 cmp dword ptr [ebp-4],0 
00000028 sete al 
0000002b movzx eax,al 
0000002e mov esp,ebp 
00000030 pop ebp 
00000031 ret 

आप देख सकते हैं, लंबाई के लिए कि कोड == 0स्ट्रिंग.इएसनुलऑरिएक्टी के लिए कोड करता है जो कुछ भी करता है, लेकिन इसके अतिरिक्त यह बेवकूफ रूप से बूलियन मान को परिवर्तित करने की कोशिश करता है (लंबाई तुलना से लौटाया गया) aga बुलियन में और इससे स्ट्रिंग.इएसनुलऑरिएक्टी से धीमा हो जाता है।

यदि आप अनुकूलन सक्षम (रिलीज मोड) के साथ प्रोग्राम संकलित करते हैं और सीधे विंडोज से .exe फ़ाइल चलाते हैं, तो जेआईटी कंपाइलर द्वारा उत्पन्न कोड बहुत बेहतर है। स्ट्रिंग के लिए।IsNullOrEmpty यह है:

001f0650 push ebp 
001f0651 mov  ebp,esp 
001f0653 test ecx,ecx 
001f0655 je  001f0663 
001f0657 cmp  dword ptr [ecx+4],0 
001f065b sete al 
001f065e movzx eax,al 
001f0661 jmp  001f0668 
001f0663 mov  eax,1 
001f0668 and  eax,0FFh 
001f066d pop  ebp 
001f066e ret 

और लंबाई == 0 के लिए: के रूप में उम्मीद कर रहे हैं

001406f0 cmp  dword ptr [ecx+4],0 
001406f4 sete al 
001406f7 movzx eax,al 
001406fa ret 
इस कोड के साथ

, परिणाम, यानी लंबाई == 0स्ट्रिंग की तुलना में थोड़ा तेज है ISNullOrEmpty

यह भी उल्लेखनीय है कि आपके बेंचमार्क में लिंक, लैम्ब्डा एक्सप्रेशन और कंप्यूटिंग मॉड्यूल का उपयोग करना इतना अच्छा विचार नहीं है, क्योंकि ये ऑपरेशन धीमे होते हैं (अपेक्षाकृत स्ट्रिंग तुलना करने के लिए) और बेंचमार्क गलत परिणाम देते हैं।

+0

मुझे वही परिणाम मिलते हैं, चाहे मैं परीक्षण चलाता हूं, मैंने विजुअल स्टूडियो के अंदर या बाहर परीक्षण चलाया। दोनों मामलों में, मैं .NET Framework 4 के विरुद्ध रिलीज़ मोड में निर्माण कर रहा हूं, और प्रोजेक्ट फ़ाइल (इसकी डिफ़ॉल्ट) में "ऑप्टिमाइज़ कोड" सेटिंग चालू है। विजुअल स्टूडियो में, मुझे आपके द्वारा पोस्ट किए गए अपरिवर्तित असेंबली कोड दिखाई देते हैं।विजुअल स्टूडियो के बाहर चलते समय उत्पन्न होने वाले असेंबली कोड को आप कैसे देखते हैं? –

+0

यह अजीब बात है। मैंने विभिन्न सीपीयू के साथ विभिन्न ओएस (विंडोज सर्वर 2008 x64, विंडोज एक्सपी x86) के साथ 3 अलग-अलग कंप्यूटरों पर इस बेंचमार्क का परीक्षण किया और मुझे हमेशा लगता है कि लंबाई == 0 तेज है। इसके अतिरिक्त मैंने इस परियोजना के लिए विजुअल स्टूडियो में पीडीबी फाइलों की पीढ़ी को बंद कर दिया, लेकिन शायद यह कोई मुद्दा नहीं है। क्या आपने एक और कंप्यूटर कोशिश की? मैं अनुकूलित विधानसभा कोड देखने के लिए [WinDbg] (http://archive.msdn.microsoft.com/debugtoolswindows) के साथ चलने की प्रक्रिया से जुड़ा हुआ हूं। –

+0

मैं वीएस -2010 के अंदर बनाम बनाम के प्रयोगों को दोबारा शुरू करता हूं और मेरी पिछली टिप्पणी में जो कुछ भी मैंने बताया है उसे पुन: उत्पन्न नहीं कर सका। अब मैं आपके जैसा ही देखता हूं, कि वीएस -2010 के बाहर जब 'लंबाई' तेज है। मुझे यह सेटिंग भी याद आई: टूल्स> विकल्प> डिबगिंग> सामान्य> मॉड्यूल लोड पर जेआईटी अनुकूलन को दबाएं। मैं इसे बंद करना भूल गया था। जब मैंने किया, तो मुझे वही परिणाम VS2010 में और बाहर मिलते हैं, जिसमें 'लंबाई' तेज होती है। इसके अतिरिक्त, जेआईटी ऑप्टिमाइज़ेशन सेटिंग को टॉगल करके, मैं आपके सभी असेंबली कोड लिस्टिंग को VS2010 के भीतर पुन: पेश कर सकता हूं। –

-4

यह शामिल चर के प्रकार के कारण हो सकता है। * खाली एक बुलियन का उपयोग करने लगता है, एक int लंबाई (मुझे लगता है)।

शांति!

  • : संपादित
+3

-1। और गलत तरीके से अनुमान लगाया। जबकि आपको निश्चित रूप से इस मंच में अपना ज्ञान साझा करने के लिए प्रोत्साहित किया जाता है, एक उत्तर में अनुमान लगाने से हमारे भाषण में अवांछनीय शोर शामिल होता है। –

1

आप परीक्षण गलत somethere है। IsNullOrEmpty परिभाषा द्वारा तेज़ नहीं हो सकता है, क्योंकि यह अतिरिक्त शून्य तुलना ऑपरेशन करता है, और फिर लंबाई का परीक्षण करता है।

तो उत्तर हो सकता है: यह आपके परीक्षण की वजह से तेज़ है। हालांकि यहां तक ​​कि आपका कोड दिखाता है कि IsNullOrEmpty मेरी मशीन पर x86 और x64 दोनों मोड में लगातार धीमी है।

+0

मेरा मानना ​​है कि IsNullOrEmpty एक नल स्ट्रिंग के मामले में तेज़ी से निष्पादित कर सकता है, क्योंकि लंबाई जांच नहीं की जाती है। हालांकि मुझे संदेह है कि प्रदर्शन में किसी भी सराहनीय प्रदर्शन में वृद्धि देखी जाएगी, अगर स्ट्रिंग को अक्सर शून्य होने की उम्मीद है, तो यह जांच अधिक समझ में आ सकती है। – overslacked

+1

मेरा मानना ​​है कि 'शून्य' तारों के मामले के बारे में बात करने के लिए मान्य नहीं है क्योंकि 'उस मामले में सभी तरंगदैर्ध्य लागू नहीं हैं :) –

+0

टच! । । । । – overslacked

4

आपका बेंचमार्क स्ट्रिंग को मापता नहीं है। INullOrEmpty बनाम स्ट्रिंग। लम्बाई, बल्कि कार्यों के लिए विभिन्न लैम्ब्डा अभिव्यक्तियां कैसे उत्पन्न होती हैं। अर्थात। यह आश्चर्य की बात नहीं है कि प्रतिनिधि जो केवल एक फंक्शन कॉल (IsNullOrEmpty) शामिल है, फ़ंक्शन कॉल और तुलना (लंबाई == 0) के साथ एक से अधिक तेज़ है।

actuall कॉल की तुलना करने के लिए - लिखें कोड जो उन्हें बिना प्रतिनिधियों के सीधे कॉल करता है।

संपादित करें: मेरा मोटा माप दिखाता है कि IsNullOrEmpty के साथ प्रतिनिधि संस्करण थोड़ा तेज़ है, जबकि उसी तुलना में सीधी कॉल रिवर्स ऑर्डर (और अतिरिक्त कोड की काफी कम संख्या के कारण लगभग दो गुना तेजी से होती है) । परिणाम मशीनों, x86/x64 मोड, साथ ही रनटाइम के संस्करणों के बीच सावधान होने की संभावना है। व्यावहारिक उद्देश्यों के लिए, यदि आप LINQ प्रश्नों में उनका उपयोग करने की आवश्यकता है तो मैं सभी 4 तरीकों के बारे में सोचूंगा।

कुल मिलाकर मुझे संदेह है कि इन तरीकों के बीच पसंद के आधार पर वास्तविक कार्यक्रम में मापनीय अंतर होगा, इसलिए उस व्यक्ति को चुनें जो आपके लिए सबसे अधिक पठनीय है और इसका उपयोग करें। मैं आम तौर पर IsNullOrEmpty पसंद करता हूं क्योंकि यह किसी स्थिति में == /! = गलत होने का कम मौका देता है।

स्ट्रिंग मैनिप्ल्यूशन को समय-समय पर गंभीर कोड से हटाने से तुलनात्मक रूप से इन विकल्पों के बीच चयन करने वाले बहुत अधिक लाभकारी लाभ मिलेगा, जो गंभीर कोड के लिए LINQ को छोड़कर एक विकल्प है। हमेशा के रूप में - वास्तविक जीवन परिदृश्य में समग्र कार्यक्रम की गति को मापने के लिए सुनिश्चित करें।

1

मेरा मानना ​​है कि अपने परीक्षण सही नहीं है:

इस परीक्षण से पता चलता है कि string.IsNullOrEmpty हमेशा s.Length==0 धीमी गति से है, क्योंकि यह एक अतिरिक्त अशक्त जाँच करता है:

var strings = "A,B,,C,DE,F,,G,H,,,,I,J,,K,L,MN,OP,Q,R,STU,V,W,X,Y,Z,".Split(','); 
var testers = new Func<string, bool>[] { 
    s => s == String.Empty, 
    s => s.Length == 0, 
    s => String.IsNullOrEmpty(s), 
    s => s == "" , 
}; 
int n = testers.Length; 
var stopwatches = Enumerable.Range(0, testers.Length).Select(_ => new Stopwatch()).ToArray(); 
int count = 0; 
for(int i = 0; i < n; ++i) { // iterate testers one by one 
    Stopwatch sw = stopwatches[i]; 
    var tester = testers[i]; 
    sw.Start(); 
    for(int j = 0; j < 10000000; ++j) // increase this count for better precision 
     count += strings.Count(tester); 
    sw.Stop(); 
} 
for(int i = 0; i < testers.Length; i++) 
    Console.WriteLine(stopwatches[i].ElapsedMilliseconds); 

परिणाम:

6573 
5328 
5488 
6419 

आप s.Length==0 का उपयोग कर सकते हैं जब आप सुनिश्चित कर लें कि लक्ष्य डेटा में नल स्ट्रिंग्स नहीं हैं। अन्य मामलों में मेरा सुझाव है कि आप String.IsNullOrEmpty का उपयोग करें।

+0

जब मैं इस तरह से परीक्षण की संरचना करता हूं, तो मुझे एक ही परिणाम मिलते हैं, लेकिन परीक्षणों के बीच उच्च मानक विचलन होता है। मुझे लगता है कि ऐसा इसलिए है क्योंकि अन्य प्रक्रियाओं या ओएस कोड के लिए एक परीक्षक को गलत तरीके से प्रभावित करना आसान है। औसतन, मैं अभी भी x86 पर 'IsNullOrEmpty' के साथ समाप्त होता हूं। मैं 64-बिट कोर i5 पर चल रहा हूं। क्या आप लगातार 'लंबाई' तेज पाते हैं? –

0

मुझे लगता है कि यह तेजी से IsNullOrEmpty असंभव है क्योंकि बाकी सभी ने कहा कि यह शून्य के लिए भी जांच करता है। लेकिन तेजी से या अंतर इतना छोटा नहीं होगा, यह इस अतिरिक्त शून्य जांच के कारण IsNullOrEmpty का उपयोग करने पर एक प्लस देता है जो आपके कोड को सुरक्षित बनाता है।

-2

CLR via CSharp अध्याय 10 "गुण" में जेफ रिक्टर लिखते हैं:

एक संपत्ति विधि पर अमल करने में लंबा समय लग सकता है; क्षेत्र का उपयोग हमेशा पूरा हो जाता है। गुणों का उपयोग करने का एक आम कारण थ्रेड सिंक्रनाइज़ेशन करना है, जो थ्रेड को हमेशा के लिए रोक सकता है, और इसलिए, थ्रेड सिंक्रनाइज़ेशन की आवश्यकता होने पर एक संपत्ति का उपयोग नहीं किया जाना चाहिए। उस स्थिति में, एक विधि को प्राथमिकता दी जाती है। साथ ही, यदि आपकी कक्षा को दूरस्थ रूप से एक्सेस किया जा सकता है (उदाहरण के लिए, आपकी कक्षा System.MarshalByRefObject से ली गई है), तो संपत्ति विधि को कॉल करना बहुत धीमा होगा, और इसलिए, किसी विधि को एक विधि पसंद की जाती है। मेरी राय में, MarshalByRefObject से प्राप्त कक्षाओं का कभी भी गुणों का उपयोग नहीं करना चाहिए।

तो अगर हम देखते हैं String.Length संपत्ति है और String.IsNullOrEmpty एक तरीका है जिसके संपत्ति String.Length तुलना में तेजी से अमल हो सकता है।

+1

एक संपत्ति केवल मेटाडेटा के रूप में मौजूद है। जब आप संपत्ति को "प्राप्त" करते हैं, तो यह 'get_PropertyName' नामक एक नियमित विधि को कॉल करता है, और जब आप संपत्ति को "सेट" करते हैं तो यह' set_PropertyName' नामक एक नियमित विधि कहता है। जेआईटी और निष्पादन समय के परिप्रेक्ष्य से, संपत्ति और विधि के बीच कोई अंतर नहीं है। –

संबंधित मुद्दे