मेरे कोड को गति देने का प्रयास करें?


1503

मैंने कोशिश-कैच के प्रभाव के परीक्षण के लिए कुछ कोड लिखे, लेकिन कुछ आश्चर्यजनक परिणाम देखे।

static void Main(string[] args)
{
    Thread.CurrentThread.Priority = ThreadPriority.Highest;
    Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.RealTime;

    long start = 0, stop = 0, elapsed = 0;
    double avg = 0.0;

    long temp = Fibo(1);

    for (int i = 1; i < 100000000; i++)
    {
        start = Stopwatch.GetTimestamp();
        temp = Fibo(100);
        stop = Stopwatch.GetTimestamp();

        elapsed = stop - start;
        avg = avg + ((double)elapsed - avg) / i;
    }

    Console.WriteLine("Elapsed: " + avg);
    Console.ReadKey();
}

static long Fibo(int n)
{
    long n1 = 0, n2 = 1, fibo = 0;
    n++;

    for (int i = 1; i < n; i++)
    {
        n1 = n2;
        n2 = fibo;
        fibo = n1 + n2;
    }

    return fibo;
}

मेरे कंप्यूटर पर, यह लगभग 0.96 के मूल्य को प्रिंट करता है।

जब मैं फिबो के अंदर लूप के लिए लपेटता हूं () इस तरह की कोशिश करने वाले ब्लॉक के साथ:

static long Fibo(int n)
{
    long n1 = 0, n2 = 1, fibo = 0;
    n++;

    try
    {
        for (int i = 1; i < n; i++)
        {
            n1 = n2;
            n2 = fibo;
            fibo = n1 + n2;
        }
    }
    catch {}

    return fibo;
}

अब यह लगातार 0.69 प्रिंट करता है ... - यह वास्तव में तेजी से चलता है! लेकिन क्यों?

नोट: मैंने इसे रिलीज़ कॉन्फ़िगरेशन का उपयोग करके संकलित किया है और सीधे EXE फ़ाइल (विज़ुअल स्टूडियो के बाहर) चलाई है।

EDIT: जॉन स्कीट के उत्कृष्ट विश्लेषण से पता चलता है कि ट्राइ-कैच किसी तरह से x86 सीएलआर के कारण इस विशिष्ट मामले में अधिक अनुकूल तरीके से सीपीयू रजिस्टरों का उपयोग कर रहा है (और मुझे लगता है कि हमें अभी तक समझ में क्यों नहीं आया है)। मैंने जॉन की खोज की पुष्टि की कि x64 सीएलआर में यह अंतर नहीं है, और यह x86 सीएलआर की तुलना में तेज था। मैंने intFibo पद्धति के अंदर के longप्रकारों के बजाय प्रकारों का उपयोग करके भी परीक्षण किया , और फिर x86 सीएलआर x64 सीएलआर के समान तेज था।


अद्यतन: ऐसा लगता है कि यह मुद्दा रोजलिन द्वारा तय किया गया है। एक ही मशीन, एक ही सीएलआर संस्करण - मुद्दा वीएस 2013 के साथ संकलित किए जाने पर ऊपर रहता है, लेकिन वीएस 2015 के साथ संकलित होने पर समस्या दूर हो जाती है।


111
@ लॉयड वह अपने सवाल पर जवाब पाने की कोशिश करता है "यह वास्तव में तेजी से चलता है! लेकिन क्यों?"
एंड्रियास नीडेर्मेयर

137
तो, अब "निगलने वाले अपवाद" एक अच्छा प्रदर्शन अनुकूलन के लिए एक बुरा अभ्यास होने से पारित हुआ: पी
लुसियानो

2
क्या यह अनियंत्रित या जाँच अंकगणित संदर्भ में है?
रैंडम 832

7
@ taras.roshko: जबकि मैं एरिक को असंतुष्ट करने की इच्छा नहीं करता, लेकिन यह वास्तव में C # प्रश्न नहीं है - यह एक JIT कंपाइलर प्रश्न है। अंतिम कठिनाई काम कर रही है क्यों x86 JIT कोशिश / पकड़ के बिना के रूप में कई रजिस्टरों का उपयोग नहीं करता है क्योंकि यह कोशिश / पकड़ ब्लॉक के साथ करता है ।
जॉन स्कीट

63
मीठा है, तो अगर हम इन कोशिशों को पकड़ते हैं तो हम और भी तेजी से सही हो सकते हैं?
चक पिंकर्ट

जवाबों:


1053

रोज़लिन इंजीनियरों में से एक, जो स्टैक के उपयोग के अनुकूलन को समझने में माहिर थे, ने इस पर एक नज़र डाली और मुझे सूचित किया कि सी # कंपाइलर स्थानीय वैरिएबल स्टोर और जिस तरह से जेआईटी कंपाइलर रजिस्टर करता है, उसके बीच की बातचीत में एक समस्या लगती है। इसी x86 कोड में समयबद्धन। परिणाम स्थानीय लोगों के भार और भंडार पर उप-अपनाने योग्य कोड पीढ़ी है।

किसी कारण से हम सभी के लिए अस्पष्ट, समस्याग्रस्त कोड जनरेशन पथ से बचा जाता है जब JITTER जानता है कि ब्लॉक एक सुरक्षित क्षेत्र में है।

यह काफी अजीब है। हम JITTER टीम के साथ अनुसरण करेंगे और देखेंगे कि क्या हमें बग दर्ज किया जा सकता है ताकि वे इसे ठीक कर सकें।

इसके अलावा, हम रोज़लिन के लिए C # और VB कंपाइलरों के एल्गोरिदम के निर्धारण के लिए सुधार पर काम कर रहे हैं, जब स्थानीय लोगों को "पंचांग" बनाया जा सकता है - यानी, स्टैक पर एक विशिष्ट स्थान आवंटित करने के बजाय केवल स्टैक पर धकेल दिया और पॉप किया जाएगा। सक्रियण की अवधि। हमारा मानना ​​है कि JITTER रजिस्टर आवंटन का एक बेहतर काम करने में सक्षम होगा और अगर स्थानीय लोगों को पहले "मृत" बनाया जा सकता है, तो हम इसे बेहतर संकेत देते हैं।

इसे हमारे ध्यान में लाने के लिए धन्यवाद, और विषम व्यवहार के लिए क्षमा याचना।


8
मैंने हमेशा सोचा है कि C # संकलक इतने सारे बाहरी लोगों को क्यों उत्पन्न करता है। उदाहरण के लिए, नए सरणी आरंभीकरण के भाव हमेशा एक स्थानीय उत्पन्न करते हैं, लेकिन कभी भी स्थानीय उत्पन्न करने के लिए आवश्यक नहीं है। यदि यह JITTER को अधिक शक्तिशाली कोड उत्पन्न करने की अनुमति देता है, तो शायद C # संकलक अनावश्यक लोकल जनरेट करने के बारे में थोड़ा और अधिक सावधानी बरतें ...
Timwi

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

24
क्या इस मुद्दे पर कभी कोई आंदोलन हुआ था?
रॉबर्ट हार्वे

10
ऐसा लग रहा है कि रोजलिन ने इसे ठीक किया।
एरेन एर्सोन्मेज़

56
आपने इसे "JITTER बग" कहने का अपना अवसर गंवा दिया।
mbomb007 20

734

ठीक है, जिस तरह से आप चीजों को टाइम कर रहे हैं वह मुझे बहुत बुरा लग रहा है। यह पूरी तरह से पूरे लूप के लिए बहुत अधिक समझदार होगा:

var stopwatch = Stopwatch.StartNew();
for (int i = 1; i < 100000000; i++)
{
    Fibo(100);
}
stopwatch.Stop();
Console.WriteLine("Elapsed time: {0}", stopwatch.Elapsed);

इस तरह आप छोटे समय, दयापूर्ण अंकगणित और संचित त्रुटि की दया पर नहीं हैं।

उस परिवर्तन को करने के बाद, देखें कि क्या "नॉन-कैच" संस्करण "कैच" संस्करण की तुलना में अभी भी धीमा है।

संपादित करें: ठीक है, मैंने इसे स्वयं आजमाया है - और मैं उसी परिणाम को देख रहा हूं। बहुत अजीब। मुझे आश्चर्य है कि क्या कोशिश / पकड़ कुछ खराब inlining को अक्षम कर रही [MethodImpl(MethodImplOptions.NoInlining)]थी , लेकिन इसके बजाय उपयोग करने से मदद नहीं मिली ...

मूल रूप से आपको कॉर्डबग के तहत अनुकूलित JITted कोड को देखने की आवश्यकता होगी, मुझे संदेह है ...

संपादित करें: जानकारी के कुछ और बिट्स:

  • बस n++;लाइन के आसपास की कोशिश / पकड़ लाना अभी भी प्रदर्शन में सुधार करता है, लेकिन पूरे ब्लॉक के आसपास इसे डालकर नहीं
  • यदि आप एक विशिष्ट अपवाद को पकड़ते हैं ( ArgumentExceptionमेरे परीक्षणों में) यह अभी भी तेज है
  • यदि आप कैच ब्लॉक में अपवाद प्रिंट करते हैं तो यह अभी भी तेज है
  • यदि आप कैच ब्लॉक में अपवाद को हटा देते हैं तो यह फिर से धीमा हो जाता है
  • यदि आप कैच ब्लॉक के बजाय अंततः ब्लॉक का उपयोग करते हैं तो यह फिर से धीमा हो जाता है
  • यदि आप एक अंतिम ब्लॉक के साथ-साथ एक कैच ब्लॉक का उपयोग करते हैं, तो यह तेज है

अजीब...

संपादित करें: ठीक है, हम disassembly है ...

यह C # 2 कंपाइलर और .NET 2 (32-बिट) सीएलआर का उपयोग कर रहा है, mdbg के साथ असंतुष्ट (जैसा कि मेरी मशीन पर कॉर्डबग नहीं है)। मैं अभी भी डिबगर के तहत समान प्रदर्शन प्रभाव देखता हूं। फास्ट संस्करण tryकेवल एक catch{}हैंडलर के साथ चर घोषणाओं और रिटर्न स्टेटमेंट के बीच सब कुछ के आसपास एक ब्लॉक का उपयोग करता है । स्पष्ट रूप से धीमा संस्करण कोशिश / पकड़ के बिना समान है। कॉलिंग कोड (यानी मुख्य) दोनों मामलों में समान है, और इसमें एक ही विधानसभा प्रतिनिधित्व है (इसलिए यह एक अशुद्धि मुद्दा नहीं है)।

तेज संस्करण के लिए डिसबेल्ड कोड:

 [0000] push        ebp
 [0001] mov         ebp,esp
 [0003] push        edi
 [0004] push        esi
 [0005] push        ebx
 [0006] sub         esp,1Ch
 [0009] xor         eax,eax
 [000b] mov         dword ptr [ebp-20h],eax
 [000e] mov         dword ptr [ebp-1Ch],eax
 [0011] mov         dword ptr [ebp-18h],eax
 [0014] mov         dword ptr [ebp-14h],eax
 [0017] xor         eax,eax
 [0019] mov         dword ptr [ebp-18h],eax
*[001c] mov         esi,1
 [0021] xor         edi,edi
 [0023] mov         dword ptr [ebp-28h],1
 [002a] mov         dword ptr [ebp-24h],0
 [0031] inc         ecx
 [0032] mov         ebx,2
 [0037] cmp         ecx,2
 [003a] jle         00000024
 [003c] mov         eax,esi
 [003e] mov         edx,edi
 [0040] mov         esi,dword ptr [ebp-28h]
 [0043] mov         edi,dword ptr [ebp-24h]
 [0046] add         eax,dword ptr [ebp-28h]
 [0049] adc         edx,dword ptr [ebp-24h]
 [004c] mov         dword ptr [ebp-28h],eax
 [004f] mov         dword ptr [ebp-24h],edx
 [0052] inc         ebx
 [0053] cmp         ebx,ecx
 [0055] jl          FFFFFFE7
 [0057] jmp         00000007
 [0059] call        64571ACB
 [005e] mov         eax,dword ptr [ebp-28h]
 [0061] mov         edx,dword ptr [ebp-24h]
 [0064] lea         esp,[ebp-0Ch]
 [0067] pop         ebx
 [0068] pop         esi
 [0069] pop         edi
 [006a] pop         ebp
 [006b] ret

धीमे संस्करण के लिए डिसबेल्ड कोड:

 [0000] push        ebp
 [0001] mov         ebp,esp
 [0003] push        esi
 [0004] sub         esp,18h
*[0007] mov         dword ptr [ebp-14h],1
 [000e] mov         dword ptr [ebp-10h],0
 [0015] mov         dword ptr [ebp-1Ch],1
 [001c] mov         dword ptr [ebp-18h],0
 [0023] inc         ecx
 [0024] mov         esi,2
 [0029] cmp         ecx,2
 [002c] jle         00000031
 [002e] mov         eax,dword ptr [ebp-14h]
 [0031] mov         edx,dword ptr [ebp-10h]
 [0034] mov         dword ptr [ebp-0Ch],eax
 [0037] mov         dword ptr [ebp-8],edx
 [003a] mov         eax,dword ptr [ebp-1Ch]
 [003d] mov         edx,dword ptr [ebp-18h]
 [0040] mov         dword ptr [ebp-14h],eax
 [0043] mov         dword ptr [ebp-10h],edx
 [0046] mov         eax,dword ptr [ebp-0Ch]
 [0049] mov         edx,dword ptr [ebp-8]
 [004c] add         eax,dword ptr [ebp-1Ch]
 [004f] adc         edx,dword ptr [ebp-18h]
 [0052] mov         dword ptr [ebp-1Ch],eax
 [0055] mov         dword ptr [ebp-18h],edx
 [0058] inc         esi
 [0059] cmp         esi,ecx
 [005b] jl          FFFFFFD3
 [005d] mov         eax,dword ptr [ebp-1Ch]
 [0060] mov         edx,dword ptr [ebp-18h]
 [0063] lea         esp,[ebp-4]
 [0066] pop         esi
 [0067] pop         ebp
 [0068] ret

प्रत्येक मामले में *डिबगर साधारण "स्टेप-इन" में प्रवेश करता है।

संपादित करें: ठीक है, मैंने अब कोड के माध्यम से देखा है और मुझे लगता है कि मैं देख सकता हूं कि प्रत्येक संस्करण कैसे काम करता है ... और मेरा मानना ​​है कि धीमी संस्करण धीमा है क्योंकि यह कम रजिस्टरों और अधिक स्टैक स्पेस का उपयोग करता है। के छोटे मूल्यों nके लिए संभवतः तेज है - लेकिन जब लूप समय के थोक लेता है, यह धीमा है।

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

संपादित करें: बस मेरे x64 मशीन पर यह कोशिश की। इस कोड पर x86 सीएलआर की तुलना में x64 सीएलआर बहुत तेज (लगभग 3-4 गुना तेज) है, और x64 के तहत कोशिश / कैच ब्लॉक ध्यान देने योग्य अंतर नहीं करता है।


4
@GordonSimpson लेकिन उस मामले में जहां केवल एक विशिष्ट अपवाद पकड़ा जाता है, तब अन्य सभी अपवादों को नहीं पकड़ा जाएगा, इसलिए बिना किसी कोशिश के आपकी उपकल्पना में जो भी उपरि शामिल था, उसकी अभी भी आवश्यकता होगी।
जॉन हैना

45
यह रजिस्टर आवंटन में अंतर जैसा दिखता है। esi,ediस्टैक के बजाय लॉन्ग में से एक के लिए उपयोग करने के लिए फास्ट संस्करण का प्रबंधन करता है । यह ebxकाउंटर के रूप में उपयोग करता है , जहां धीमा संस्करण उपयोग करता है esi
जेफरी सैक्स

13
@JeffreySax: यह न सिर्फ है जो रजिस्टरों उपयोग किया जाता है लेकिन कितनी। धीमे संस्करण में अधिक स्टैक स्थान का उपयोग किया जाता है, कम रजिस्टरों को छूता है। मुझे पता नहीं क्यों ...
जॉन स्कीट

2
रजिस्टर और स्टैक के संदर्भ में सीएलआर अपवाद फ्रेम कैसे निपटाए जाते हैं? क्या किसी एक को सेट करने से किसी रजिस्टर को किसी भी तरह इस्तेमाल करने से मुक्ति मिल सकती है?
रैंडम 832

4
IIRC x64 में x86 की तुलना में अधिक रजिस्टर उपलब्ध हैं। आपने जो स्पीडअप देखा, वह x86 के तहत अतिरिक्त रजिस्टर उपयोग के प्रयास / पकड़ के अनुरूप होगा।
डैन इज़ फ़िडलिंग बाय फायरलाइट

116

जॉन की असहमति दिखाती है, कि दो संस्करणों के बीच का अंतर यह है कि तेज संस्करण रजिस्टरों की एक जोड़ी का उपयोग करता है ( esi,edi) जहां एक धीमी गति से संस्करण नहीं होता है।

JIT कंपाइलर कोड के लिए रजिस्टर उपयोग के बारे में अलग-अलग धारणाएं बनाता है जिसमें एक कोशिश-पकड़ने वाला ब्लॉक बनाम कोड होता है जो नहीं करता है। यह विभिन्न रजिस्टर आवंटन विकल्प बनाने का कारण बनता है। इस मामले में, यह कोशिश-पकड़ने वाले ब्लॉक के साथ कोड का पक्षधर है। विभिन्न कोड विपरीत प्रभाव को जन्म दे सकते हैं, इसलिए मैं इसे सामान्य-उद्देश्य गति-तकनीक के रूप में नहीं गिना जा सकता।

अंत में, यह बताना बहुत कठिन है कि कौन सा कोड सबसे तेजी से चलना समाप्त करेगा। रजिस्टर आबंटन और इसे प्रभावित करने वाले कारकों की तरह कुछ इस तरह के निम्न-स्तरीय कार्यान्वयन विवरण हैं कि मैं यह नहीं देखता कि कोई भी विशिष्ट तकनीक कैसे तेजी से कोड का उत्पादन कर सकती है।

उदाहरण के लिए, निम्नलिखित दो विधियों पर विचार करें। उन्हें वास्तविक जीवन के उदाहरण से अनुकूलित किया गया था:

interface IIndexed { int this[int index] { get; set; } }
struct StructArray : IIndexed { 
    public int[] Array;
    public int this[int index] {
        get { return Array[index]; }
        set { Array[index] = value; }
    }
}

static int Generic<T>(int length, T a, T b) where T : IIndexed {
    int sum = 0;
    for (int i = 0; i < length; i++)
        sum += a[i] * b[i];
    return sum;
}
static int Specialized(int length, StructArray a, StructArray b) {
    int sum = 0;
    for (int i = 0; i < length; i++)
        sum += a[i] * b[i];
    return sum;
}

एक दूसरे का एक सामान्य संस्करण है। जेनेरिक प्रकार को प्रतिस्थापित StructArrayकरने के तरीके समान होंगे। क्योंकि StructArrayएक मूल्य प्रकार है, यह सामान्य विधि का अपना संकलित संस्करण प्राप्त करता है। फिर भी वास्तविक चलने का समय विशेष विधि की तुलना में काफी लंबा है, लेकिन केवल x86 के लिए। X64 के लिए, समय बहुत अधिक समान हैं। अन्य मामलों में, मैंने x64 के लिए अंतर भी देखा है।


6
उस के साथ कहा जा रहा है ... क्या आप एक कोशिश / पकड़ का उपयोग किए बिना विभिन्न पंजीकरण आवंटन विकल्पों को मजबूर कर सकते हैं? या तो इस परिकल्पना के लिए एक परीक्षण के रूप में या गति के लिए एक सामान्य प्रयास के रूप में?
वर्नरसीडी

1
इस विशिष्ट मामले के भिन्न होने के कई कारण हो सकते हैं। शायद यह कोशिश है। हो सकता है कि यह तथ्य है कि चर को फिर से एक आंतरिक दायरे में उपयोग किया जाता है। जो भी विशिष्ट कारण है, यह एक कार्यान्वयन विवरण है जिसे आप गणना नहीं कर सकते भले ही एक ही कोड को एक अलग कार्यक्रम में कहा जाए।
जेफरी सैक्स

4
@WernerCD मैं इस तथ्य को कहूंगा कि C और C ++ में सुझाव देने के लिए एक कीवर्ड है, जिसे (A) कई आधुनिक संकलक द्वारा अनदेखा किया गया है और (B) यह निर्णय लिया गया था कि इसे C # में न रखा जाए, यह बताता है कि यह कुछ ऐसा नहीं है जिसे हम ' किसी भी अधिक प्रत्यक्ष तरीके से देखेंगे।
जॉन हैना

2
@WernerCD - केवल अगर आप खुद विधानसभा लिखते हैं
OrangeDog

72

ऐसा लग रहा है कि इनलाइनिंग खराब हो गई है। एक x86 कोर पर, घबराना के पास स्थानीय चर के सामान्य प्रयोजन भंडारण के लिए ईबेक्स, ईएक्सएक्स, एसआई और एडी रजिस्टर उपलब्ध है। ECX रजिस्टर एक स्थिर विधि में उपलब्ध हो जाता है, यह स्टोर करने के लिए नहीं है यह । गणना के लिए अक्सर ईएक्स रजिस्टर की आवश्यकता होती है। लेकिन ये 32-बिट रजिस्टर हैं, लंबे प्रकार के चर के लिए इसे रजिस्टर की एक जोड़ी का उपयोग करना चाहिए। जो edx हैं: गणना के लिए ईएक्सएक्स और ईडीआई: भंडारण के लिए ईएक्सएक्स।

जो कि धीमे संस्करण के लिए डिस्सैम्ड में खड़ा है, न तो ईडीआई और न ही ईबेक्स का उपयोग किया जाता है।

जब घबराना स्थानीय चरों को संग्रहीत करने के लिए पर्याप्त रजिस्टर नहीं पा सकता है, तो उसे स्टैक फ्रेम से लोड और स्टोर करने के लिए कोड उत्पन्न करना होगा। यह कोड धीमा कर देता है, यह एक प्रोसेसर ऑप्टिमाइज़ेशन को रोकता है जिसका नाम "रजिस्टर रीनेमिंग" है, एक आंतरिक प्रोसेसर कोर ऑप्टिमाइज़ेशन ट्रिक जो रजिस्टर की कई प्रतियों का उपयोग करता है और सुपर-स्केलर निष्पादन की अनुमति देता है। जो समान रजिस्टर का उपयोग करने पर भी समवर्ती चलाने के लिए कई निर्देशों की अनुमति देता है। पर्याप्त रजिस्टर न होना x86 कोर पर एक सामान्य समस्या है, जिसे x64 में संबोधित किया गया है जिसमें 8 अतिरिक्त रजिस्टर हैं (r15 के माध्यम से r9)।

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

ऐसे कई नियम हैं जो सटीक तरीके से निर्धारित करते हैं कि कोई विधि कैसे इनलेट की जा सकती है। वे बिल्कुल प्रलेखित नहीं हैं, लेकिन ब्लॉग पोस्टों में उल्लेख किया गया है। एक नियम यह है कि विधि बॉडी बहुत बड़ी होने पर ऐसा नहीं होगा। यह inlining से लाभ को हरा देता है, यह बहुत अधिक कोड उत्पन्न करता है जो L1 अनुदेश कैश में भी फिट नहीं होता है। एक और कठोर नियम जो यहां लागू होता है, वह यह है कि जब कोई प्रयास / कैच स्टेटमेंट होता है तो कोई विधि इनलाइन नहीं होगी। उस एक के पीछे की पृष्ठभूमि अपवादों का कार्यान्वयन विवरण है, वे SEH (स्ट्रक्चर एक्सेप्शन हैंडलिंग) के लिए विंडोज-बिल्ट-इन सपोर्ट पर पिगी-बैक करते हैं, जो स्टैक-फ्रेम आधारित है।

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

तो आप तेज़ संस्करण प्राप्त करते हैं क्योंकि घबराना सामने जानता है कि विधि निकाय में कथन को पकड़ने / पकड़ने की कोशिश होती है। यह जानता है कि इसे कभी भी इनलाइन नहीं किया जा सकता है, इसलिए लंबे संस्करण के लिए भंडारण के लिए ईडीआई: ईबेक्स का उपयोग करता है। आपको धीमी संस्करण मिला क्योंकि घबराना सामने नहीं जानता था कि इनलाइनिंग काम नहीं करेगा। यह केवल विधि निकाय के लिए कोड जनरेट करने के बाद पता चला है।

फिर दोष यह है कि यह वापस नहीं गया और विधि के लिए कोड को फिर से उत्पन्न करता है। जो समझ में आता है, समय की कमी को देखते हुए इसे संचालित करना पड़ता है।

यह धीमी गति से x64 पर नहीं होता है क्योंकि एक के लिए इसमें 8 और रजिस्टर हैं। दूसरे के लिए क्योंकि यह सिर्फ एक रजिस्टर में एक लंबे समय तक स्टोर कर सकता है (जैसे रक्स)। जब आप लंबे समय के बजाय इंट का उपयोग करते हैं, तो धीमी गति से डाउन नहीं होता है क्योंकि रजिस्टरों को चुनने में घबराने में बहुत अधिक लचीलापन होता है।


21

मैंने इसे एक टिप्पणी के रूप में रखा है क्योंकि मैं वास्तव में निश्चित नहीं हूं कि यह मामला होने की संभावना है, लेकिन जैसा कि मुझे याद है कि यह कोशिश नहीं करता है / बयान के अलावा कचरा निपटान तंत्र के तरीके में संशोधन शामिल है संकलक काम करता है, जिसमें यह स्टैक से एक पुनरावर्ती तरीके से ऑब्जेक्ट मेमोरी आवंटन को साफ करता है। इस मामले में मंजूरी देने के लिए कोई वस्तु नहीं हो सकती है या लूप के लिए एक क्लोजर का गठन हो सकता है जो कचरा संग्रह तंत्र एक अलग संग्रह विधि को लागू करने के लिए पर्याप्त रूप से पहचानता है। शायद नहीं, लेकिन मैंने इसे एक उल्लेख के लायक समझा क्योंकि मैंने इसे कहीं और चर्चा करते नहीं देखा था।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.