रिलीज़ और डिबग मोड में कोड व्यवहार भिन्न क्यों है?


84

निम्नलिखित कोड पर विचार करें:

private static void Main(string[] args)
{
    var ar = new double[]
    {
        100
    };

    FillTo(ref ar, 5);
    Console.WriteLine(string.Join(",", ar.Select(a => a.ToString()).ToArray()));
}

public static void FillTo(ref double[] dd, int N)
{
    if (dd.Length >= N)
        return;

    double[] Old = dd;
    double d = double.NaN;
    if (Old.Length > 0)
        d = Old[0];

    dd = new double[N];

    for (int i = 0; i < Old.Length; i++)
    {
        dd[N - Old.Length + i] = Old[i];
    }
    for (int i = 0; i < N - Old.Length; i++)
        dd[i] = d;
}

डिबग मोड में परिणाम है: 100,100,100,100,100। लेकिन रिलीज मोड में यह है: 100,100,100,100,0।

क्या हो रहा है?

यह .NET फ्रेमवर्क 4.7.1 और .NET कोर 2.0.0 का उपयोग करके परीक्षण किया गया था।


विजुअल स्टूडियो (या संकलक) का कौन सा संस्करण आप उपयोग करते हैं?
स्टैक्नीडी

9
रेप्रो; Console.WriteLine(i);अंतिम लूप में जोड़कर ( dd[i] = d;) इसे "फिक्स" करता है, जो एक कंपाइलर बग या जेआईटी बग का सुझाव देता है; आईएल ... में देख
मार्क Gravell

@Styxxy, vs2015, 2017 पर परीक्षण किया गया और हर .net फ्रेमवर्क> = 4.5 को लक्षित किया गया
Ashkan Nourzadeh

निश्चित रूप से एक बग। यदि आप हटाते हैं if (dd.Length >= N) return;, तो यह भी गायब हो जाता है , जो एक सरल रीप्रो हो सकता है।
जेरोइन मोस्टर्ट

1
यह आश्चर्य की बात नहीं है कि एक बार तुलना सेब-से-सेब, .64 फ्रेमवर्क के लिए .64 फ्रेमवर्क और .नेट कोर के समान प्रदर्शन है, क्योंकि (डिफ़ॉल्ट रूप से) यह अनिवार्य रूप से एक ही जेइट जनरेटिंग कोड है। .Net कोर के x86 कोडजेन (जो 2.0 के बाद से RyuJit का उपयोग कर रहा है) के साथ .Net फ्रेमवर्क x86 कोडजेन के प्रदर्शन की तुलना करना दिलचस्प होगा। अभी भी ऐसे मामले हैं जहां पुराने जीट (उर्फ जीट 32) को कुछ तरकीबें पता हैं जो रियुजिट को नहीं आती हैं। और यदि आपको ऐसे कोई मामले मिलते हैं, तो कृपया CoreCLR रेपो पर उनके लिए मुद्दों को खोलना सुनिश्चित करें।
एंडी एयर्स

जवाबों:


70

यह एक JIT बग प्रतीत होता है; मैंने इसके साथ परीक्षण किया है:

// ... existing code unchanged
for (int i = 0; i < N - Old.Length; i++)
{
    // Console.WriteLine(i); // <== comment/uncomment this line
    dd[i] = d;
}

और Console.WriteLine(i)इसे ठीक करता है। केवल IL परिवर्तन है:

// ...
L_0040: ldc.i4.0 
L_0041: stloc.3 
L_0042: br.s L_004d
L_0044: ldarg.0 
L_0045: ldind.ref 
L_0046: ldloc.3 
L_0047: ldloc.1 
L_0048: stelem.r8 
L_0049: ldloc.3 
L_004a: ldc.i4.1 
L_004b: add 
L_004c: stloc.3 
L_004d: ldloc.3 
L_004e: ldarg.1 
L_004f: ldloc.0 
L_0050: ldlen 
L_0051: conv.i4 
L_0052: sub 
L_0053: blt.s L_0044
L_0055: ret 

बनाम

// ...
L_0040: ldc.i4.0 
L_0041: stloc.3 
L_0042: br.s L_0053
L_0044: ldloc.3 
L_0045: call void [System.Console]System.Console::WriteLine(int32)
L_004a: ldarg.0 
L_004b: ldind.ref 
L_004c: ldloc.3 
L_004d: ldloc.1 
L_004e: stelem.r8 
L_004f: ldloc.3 
L_0050: ldc.i4.1 
L_0051: add 
L_0052: stloc.3 
L_0053: ldloc.3 
L_0054: ldarg.1 
L_0055: ldloc.0 
L_0056: ldlen 
L_0057: conv.i4 
L_0058: sub 
L_0059: blt.s L_0044
L_005b: ret 

जो बिल्कुल सही दिखता है (केवल अंतर अतिरिक्त ldloc.3और call void [System.Console]System.Console::WriteLine(int32), और एक अलग लेकिन समान लक्ष्य है br.s)।

मुझे JIT फिक्स की आवश्यकता होगी, मुझे संदेह है।

वातावरण:

  • Environment.Version: 4.0.30319.42000
  • <TargetFramework>netcoreapp2.0</TargetFramework>
  • VS: 15.5.0 पूर्वावलोकन 5.0
  • dotnet --version: 2.1.1

फिर बग की रिपोर्ट कहां करें?
अश्कान नूरज़ादेह

1
मैं इसे .NET पूर्ण 4.7.1 पर भी देखता हूं, इसलिए यदि यह एक RyuJIT बग नहीं है तो मैं अपनी टोपी खाऊंगा।
जीरो मोस्टर्ट

2
मैं .NET 4.7.1 स्थापित नहीं कर सका, और अब पुन: उत्पन्न कर सकता हूं।
user3057557

3
@MarcGravell .Net फ्रेमवर्क 4.7.1 और .net Core 2.0.0
अश्कान नूरज़ादे

4
@AshkanNourzadeh शायद मैं इसे लॉग इन करेगा यहाँ , ईमानदारी से बल है कि लोगों को विश्वास है कि यह एक RyuJIT त्रुटि होने के लिए
मार्क Gravell

6

यह वास्तव में एक विधानसभा त्रुटि है। x64, .net 4.7.1, रिलीज बिल्ड।

disassembly:

            for(int i = 0; i < N - Old.Length; i++)
00007FF942690ADD  xor         eax,eax  
            for(int i = 0; i < N - Old.Length; i++)
00007FF942690ADF  mov         ebx,esi  
00007FF942690AE1  sub         ebx,ebp  
00007FF942690AE3  test        ebx,ebx  
00007FF942690AE5  jle         00007FF942690AFF  
                dd[i] = d;
00007FF942690AE7  mov         rdx,qword ptr [rdi]  
00007FF942690AEA  cmp         eax,dword ptr [rdx+8]  
00007FF942690AED  jae         00007FF942690B11  
00007FF942690AEF  movsxd      rcx,eax  
00007FF942690AF2  vmovsd      qword ptr [rdx+rcx*8+10h],xmm6  
            for(int i = 0; i < N - Old.Length; i++)
00007FF942690AF9  inc         eax  
00007FF942690AFB  cmp         ebx,eax  
00007FF942690AFD  jg          00007FF942690AE7  
00007FF942690AFF  vmovaps     xmm6,xmmword ptr [rsp+20h]  
00007FF942690B06  add         rsp,30h  
00007FF942690B0A  pop         rbx  
00007FF942690B0B  pop         rbp  
00007FF942690B0C  pop         rsi  
00007FF942690B0D  pop         rdi  
00007FF942690B0E  pop         r14  
00007FF942690B10  ret  

यह मुद्दा 00007FF942690AFD, 00007FF942690AE7 के पते पर है। यह वापस कूदता है अगर ईबेक्स (जिसमें 4, लूप एंड वैल्यू है) ईएक्स से बड़ा (जेजी) है, जो कि मूल्य है। जब यह 4 का हो तो यह विफल हो जाता है, इसलिए यह सरणी में अंतिम तत्व नहीं लिखता है।

यह विफल हो जाता है, क्योंकि यह इंक के i रजिस्टर मूल्य (eax, 0x00007FF942690AF9 पर) है, और फिर इसे 4 के साथ जांचता है, लेकिन इसे अभी भी उस मूल्य को लिखना है। जहां यह समस्या स्थित है, उसे इंगित करना थोड़ा कठिन है, क्योंकि ऐसा लगता है कि यह (N-Old.Length) के अनुकूलन का परिणाम हो सकता है, क्योंकि डिबग बिल्ड में वह कोड होता है, लेकिन रिलीज़ का निर्माण होता है। तो यह जेत लोगों को ठीक करने के लिए;)


2
इन दिनों में से एक मुझे विधानसभा / सीपीयू ऑपकोड सीखने के लिए कुछ समय निकालने की आवश्यकता है। शायद भोलेपन से मैं "हुंह, मैं पढ़ सकते हैं और लिखने आईएल - मैं इसे grok में सक्षम होना चाहिए" - सोच रखते हैं लेकिन मैं सिर्फ यह कभी नहीं करने के लिए :) आसपास
मार्क Gravell

x64 / x86 tho से शुरू होने वाली सबसे बड़ी विधानसभा भाषा नहीं है;) इसमें बहुत सारे ऑपकोड हैं, मैंने एक बार पढ़ा है कि कोई भी जीवित नहीं है जो उन सभी को जानता है। यकीन नहीं होता अगर यह सच है, लेकिन पहली बार में इसे पढ़ना इतना आसान नहीं है। यद्यपि यह कुछ सरल सम्मेलनों का उपयोग करता है, जैसे कि [], स्रोत भाग से पहले गंतव्य और इन सभी का क्या अर्थ है (अल रक्स का 8 वाँ हिस्सा है, ईएक्सएक्स रैक्स आदि का 32 वाँ हिस्सा है)। आप इसके माध्यम से बनाम थियो में कदम रख सकते हैं, जिससे आपको आवश्यक चीजें सिखनी चाहिए। मुझे यकीन है कि आप इसे जल्दी से उठा लेंगे जैसा कि आप पहले से ही आईएल ऑपकोड जानते हैं;)
क्रांस बुमा
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.