डबल? = डबल? + डबल?


79

मैं StackOverflow समुदाय को यह देखने के लिए पिंग करना चाहता था कि मैं इस सरल बिट C # कोड के साथ अपना दिमाग खो रहा हूं या नहीं।

मैं विंडोज 7 पर विकास कर रहा हूं। इसे .NET 4.0, x64 डीबग में बना रहा हूं।

मेरे पास निम्नलिखित कोड हैं:

static void Main()
{
    double? y = 1D;
    double? z = 2D;

    double? x;
    x = y + z;
}

अगर मैं डिबग करता हूं और एंडिंग कर्ली ब्रेस पर ब्रेकपॉइंट लगाता हूं, तो मुझे वॉच विंडो और इमीडिएट विंडो में x = 3 की उम्मीद है। x = इसके बजाय शून्य।

अगर मैं x86 में डिबग करता हूं, तो चीजें ठीक लगती हैं। क्या x64 संकलक के साथ कुछ गलत है या मेरे साथ कुछ गलत है?


15
यह अपने हितों के लिए प्रासंगिक है। अब हमें बस श्री स्कीट का इंतजार करना होगा।
— माइक जी

3
क्या यह डीबगर हो सकता है? अंत में एक Console.WriteLine () डालने का प्रयास करें और देखें कि यह क्या प्रिंट करता है।
— सिराइड

3
यह एक आस्थगित निष्पादन की तरह दिखता है जिसे डिबगर आगे नहीं देख सकता है। असाइनमेंट संभवतः तब तक नहीं किया जाता है जब तक कि इसे बनाने की आवश्यकता न हो।
— जोएल एथरटन

5
@igrimpe 1 और 2 के बहुत बड़े मूल्यों के लिए स्पष्ट रूप से 1 + 2 = 25?
— क्रिस सिनक्लेयर

2
सभी की प्रतिक्रिया के लिए धन्यवाद। इतनी जल्दी इस समुदाय को कूदते हुए देखना बहुत अच्छा है। X64 संकलक का व्यवहार इस प्रकार के कथनों को मेरी राय में थोड़ा अनपेक्षित बनाता है, लेकिन कम से कम ऐसा लगता है कि कोड निष्पादन एप्लिकेशन की भव्य योजना में ही ठीक से काम करता है।
— मि।

जवाबों:


86

डगलस का जवाब JIT के डेड कोड को अनुकूलित करने के बारे में सही है ( दोनों x86 और x64 कंपाइलर ऐसा करेंगे)। हालांकि, अगर JIT कंपाइलर मृत कोड का अनुकूलन कर रहा था, तो यह तुरंत स्पष्ट हो xजाएगा क्योंकि स्थानीय विंडो में भी नहीं दिखाई देगा। इसके अलावा, घड़ी और तत्काल विंडो आपको इसे एक्सेस करने का प्रयास करते समय एक त्रुटि देगा: "वर्तमान संदर्भ में 'x' नाम मौजूद नहीं है"। यह वही नहीं है जो आपने वर्णित किया है।

आप जो देख रहे हैं, वह वास्तव में Visual Studio 2010 में एक बग है।

सबसे पहले, मैंने अपने मुख्य मशीन पर इस मुद्दे को पुन: पेश करने की कोशिश की: Win7x64 और VS2012। .NET 4.0 टारगेट के लिए, x3.0D के बराबर होता है जब यह क्लोजिंग कर्ली ब्रेस पर टूटता है। मैंने .NET 3.5 लक्ष्य के रूप में अच्छी तरह से कोशिश करने का फैसला किया, और इसके साथ ही, x3.0D पर सेट किया गया, शून्य नहीं।

चूंकि मैं .NET 4.0 के शीर्ष पर स्थापित .NET 4.5 के बाद से मैं इस मुद्दे का एक पूर्ण प्रजनन नहीं कर सकता, इसलिए मैंने एक वर्चुअल मशीन को स्थापित किया और उस पर VS2010 स्थापित किया।

यहाँ, मैं इस मुद्दे को पुन: पेश करने में सक्षम था। Mainविधि के समापन घुंघराले ब्रैकेट पर एक विराम बिंदु के साथ , घड़ी की खिड़की और स्थानीय खिड़की दोनों में, मैंने देखा कि वह xथा null। यह वह जगह है जहाँ यह दिलचस्प होने लगता है। मैंने इसके बजाय v2.0 रनटाइम को लक्षित किया और पाया कि यह वहां भी अशक्त था। निश्चित रूप से ऐसा नहीं हो सकता है क्योंकि मेरे पास अपने दूसरे कंप्यूटर पर .NET 2.0 रनटाइम का समान संस्करण है जो सफलतापूर्वक xएक मान के साथ दिखाया गया है 3.0D।

तो, फिर क्या हो रहा है? विंडबग में कुछ खुदाई के बाद, मुझे यह मुद्दा मिला:

VS2010 आपको एक्स का मूल्य दिखा रहा है इससे पहले कि यह वास्तव में सौंपा गया है ।

मुझे पता है कि यह ऐसा नहीं है जैसा कि निर्देश सूचक x = y + zलाइन से अतीत है । आप विधि की कुछ पंक्तियों को जोड़कर स्वयं इसका परीक्षण कर सकते हैं:

double? y = 1D;
double? z = 2D;

double? x;
x = y + z;

Console.WriteLine(); // Don't reference x here, still leave it as dead code

अंतिम घुंघराले ब्रेस पर एक ब्रेकपॉइंट के साथ, स्थानीय और घड़ी खिड़की के शो xके बराबर है 3.0D। हालाँकि, यदि आप कोड के माध्यम से कदम बढ़ाते हैं, तो आप देखेंगे कि VS2010 आपके द्वारा दिए गए कदम के बादx असाइन किए जाने तक प्रदर्शित नहीं होता है ।Console.WriteLine()

मुझे नहीं पता कि क्या यह बग कभी Microsoft कनेक्ट को सूचित किया गया था, लेकिन आप उदाहरण के रूप में इस कोड के साथ ऐसा करना चाह सकते हैं। यह स्पष्ट रूप से VS2012 में तय किया गया है, लेकिन मुझे यकीन नहीं है कि इसे ठीक करने के लिए कोई अपडेट होगा या नहीं।


यहाँ वास्तव में JIT और VS2010 में क्या हो रहा है

मूल कोड के साथ, हम देख सकते हैं कि वीएस क्या कर रहा है और क्यों गलत है। हम यह भी देख सकते हैं कि xचर को अनुकूलित नहीं किया जा रहा है (जब तक कि आपने असेंबली को चिह्नित नहीं किया है कि वह अनुकूलन योग्य के साथ संकलित हो)।

आइए, आइएल की स्थानीय परिवर्तनशील परिभाषाओं को देखें:

.locals init (
    [0] valuetype [mscorlib]System.Nullable`1<float64> y,
    [1] valuetype [mscorlib]System.Nullable`1<float64> z,
    [2] valuetype [mscorlib]System.Nullable`1<float64> x,
    [3] valuetype [mscorlib]System.Nullable`1<float64> CS$0$0000,
    [4] valuetype [mscorlib]System.Nullable`1<float64> CS$0$0001,
    [5] valuetype [mscorlib]System.Nullable`1<float64> CS$0$0002)

यह डिबग मोड में सामान्य आउटपुट है। विजुअल स्टूडियो डुप्लिकेट लोकल वैरिएबल को परिभाषित करता है जो असाइनमेंट के दौरान उपयोग करता है, और फिर इसे CS * वैरिएबल से कॉपी करने के लिए अतिरिक्त आईएल कमांड जोड़ता है। यहाँ संगत आईएल कोड है जो यह दर्शाता है:

// For the line x = y + z
L_0045: ldloca.s CS$0$0000 // earlier, y was stloc.3 (CS$0$0000)
L_0047: call instance !0 [mscorlib]System.Nullable`1<float64>::GetValueOrDefault()
L_004c: conv.r8            // Convert to a double
L_004d: ldloca.s CS$0$0001 // earlier, z was stloc.s CS$0$0001
L_004f: call instance !0 [mscorlib]System.Nullable`1<float64>::GetValueOrDefault()
L_0054: conv.r8            // Convert to a double 
L_0055: add                // Add them together
L_0056: newobj instance void [mscorlib]System.Nullable`1<float64>::.ctor(!0) // Create a new nulable
L_005b: nop                // NOPs are placed in for debugging purposes
L_005c: stloc.2            // Save the newly created nullable into `x`
L_005d: ret 

आइए WinDbg के साथ कुछ गहरी डीबगिंग करते हैं:

यदि आप VS2010 में एप्लिकेशन को डीबग करते हैं और विधि के अंत में एक ब्रेकपॉइंट छोड़ते हैं, तो हम नॉन-इनवेसिव मोड में विनडबग को आसानी से संलग्न कर सकते हैं।

यहाँ Mainकॉल स्टैक में विधि के लिए फ्रेम है । हम आईपी (इंस्ट्रक्शन पॉइंटर) की परवाह करते हैं।

0: 009>!
ओएस थ्रेड आईडी: 0x135c (9)
बाल एसपी आईपी कॉल साइट
000000001c48dc00 000007ff0017338d ConsoleApplication1.Program.Main (System.String [])
[और इसी तरह...]

यदि हम Mainविधि के लिए मूल मशीन कोड देखते हैं, तो हम देख सकते हैं कि वीएस द्वारा निष्पादन को तोड़ने के समय क्या निर्देश चलाए गए हैं:

000007ff`00173388 e813fe25f2 कॉल mscorlib_ni + 0xd431a0 
           (000007fe`f23d31a0) (System.Nullable`1 [[System.Double, mscorlib]] .. ctor (डबल), mdToken: 0000000006001ef2)
**** 000007ff`0017338d cc int 3 ****
000007ff`0017338e 8d8c2490000000 ईएक्स एक्सएक्सएक्स, [आरएसपी + 90 एच]
000007ff`00173395 488b01 mov rax, qword ptr [rcx]
000007ff`00173398 4889842480000000 mov qword ptr [rsp + 80h], rax
000007ff`001733a0 488b4108 mov rax, qword ptr [rcx + 8]
000007ff`001733a4 4889842488000000 mov qword ptr [rsp + 88h], rax
000007ff`001733ac 488d8c2480000000 लीक आरसीएक्स, [आरएसपी + 80 h]
000007ff`001733b4 488b01 mov rax, qword ptr [rcx]
000007ff`001733b7 4889442440 mov qword ptr [rsp + 40h], rax
000007ff`001733bc 488b4108 mov rax, qword ptr [rcx + 8]
000007ff`001733c0 4889442448 mov qword ptr [rsp + 48h], rax
000007ff`001733c5 eb00 jmp 000007ff`001733c7
000007ff`001733c7 0f28b424c0000000 xmm6, xmmword ptr [rsp + 0C0h] चलता है
000007ff`001733cf 4881c4d8000000 rsp, 0D8h जोड़ें
000007ff`001733d6 c3 रिट

हमारे द्वारा प्राप्त वर्तमान आईपी का उपयोग करते !clrstackहुए Main, हम देखते हैं कि इंस्ट्रक्शन को कॉल करने के बाद सीधे इंस्ट्रक्शन पर निलंबित कर दिया गया था System.Nullable<double>। ( int 3निष्पादन को रोकने के लिए डिबगर्स द्वारा उपयोग की जाने वाली रुकावट है) मैंने उस लाइन को * के साथ घेर लिया है, और आप L_0056IL में लाइन को मेल भी कर सकते हैं ।

X64 असेंबली जो वास्तव में इसे स्थानीय चर को असाइन करती है x। हमारे निर्देश सूचक ने उस कोड को अभी तक निष्पादित नहीं किया है, इसलिए समय से पहले ही वीआर2010 xदेशी कोड द्वारा चर को सौंपा गया है।

EDIT: x64 में, int 3निर्देश असाइनमेंट कोड से पहले रखा गया है, जैसा कि आप ऊपर देख सकते हैं। X86 में, उस निर्देश को असाइनमेंट कोड के बाद रखा गया है। यह बताता है कि वीएस केवल एक्स 64 में जल्दी क्यों टूट रहा है। यह कहना मुश्किल है कि यह विजुअल स्टूडियो या JIT कंपाइलर की गलती है। मैं अनिश्चित हूं कि कौन सा एप्लिकेशन विराम बिंदु हुक सम्मिलित करता है।


6
@ChristopherCurrens ठीक आदमी है, आपने ग्रीन चेक मार्क कमाया। यह समझ में आता है। मैं Microsoft में कुछ भेजूँगा। इस पर सभी का ध्यान आकर्षित करने के लिए धन्यवाद। सामुदायिक प्रयास से बहुत प्रभावित हुए।
— 18

30

X64 की तुलना में x64 JIT संकलक को इसके अनुकूलन में अधिक आक्रामक माना जाता है। (आप एक मामले में जहां "x86 और x64 संकलक कोड उत्पन्न कर सकते हैं, जो शब्दार्थ रूप से भिन्न है, के लिए" एरियर बाउंड्स चेक एलिमिनेशन का उल्लेख कर सकते हैं ।)

इस मामले में, x64 संकलक यह पता लगा रहा है कि xकभी नहीं पढ़ा जाता है, और पूरी तरह से इसके असाइनमेंट के साथ दूर हो जाता है; इसे कंपाइलर ऑप्टिमाइज़ेशन में डेड कोड एलिमिनेशन के रूप में जाना जाता है । ऐसा होने से रोकने के लिए, असाइनमेंट के ठीक बाद निम्न पंक्ति जोड़ें:

Console.WriteLine(x);

आप देखेंगे कि न केवल का सही मूल्य नहीं करता 3मुद्रित करने के लिए, लेकिन चर xके रूप में अच्छी तरह से डीबगर में सही मान प्रदर्शित होगा (संपादित करें) निम्नलिखित Console.WriteLineकॉल जो यह संदर्भ।

संपादित करें : क्रिस्टोफर क्यूरेंस विज़ुअल स्टूडियो 2010 में बग की ओर इशारा करते हुए एक वैकल्पिक स्पष्टीकरण प्रदान करता है , जो ऊपर की तुलना में अधिक सटीक हो सकता है।


एर, मैंने इसे अभी परीक्षण किया है क्योंकि यह मेरा पहला विचार भी था, और यह उत्तर वास्तव में गलत है। डिबगर वास्तव में कॉल करने के बाद असाइनमेंट के बाद शून्य प्रदर्शित करता है Console.WriteLine()।
— टॉमफैनिंग

@tomfanning: इसका उत्तर अमान्य नहीं है। संकलक केवल उस बिंदु पर असाइनमेंट करने के निर्देश को छोड़ रहा है जहां यह पता लगाता है कि इस तरह की आवश्यकता होगी - पर Console.WriteLine।
— डगलस

1
@leppie संकलक है अलग (हुआ करता था, या कम से कम)। उदाहरण के लिए, यह x86 संकलक की तुलना में अधिक आक्रामक रूप से इनलाइन करेगा।
— कोनराड रुडोल्फ

2
@ एलेप्पी: " सीएलआर में ऐरे बाउंड्स चेक एलिमिनेशन " उदाहरण देता है कि संकलक वास्तव में अर्थ संबंधी मतभेदों को जन्म दे सकता है। “X86 और x64 के लिए JIT कंपाइलर वर्तमान में काफी भिन्न कोड बेस हैं […] x86 JIT संकलन गति के मामले में तेज है; x64 JIT धीमी है, लेकिन अधिक दिलचस्प अनुकूलन करता है। "
— डगलस

2
@MrE - इसे सही उत्तर के रूप में चिह्नित नहीं किया जाना चाहिए। यदि JIT कंपाइलर मृत कोड को दूर कर रहा था, तो दृश्य स्टूडियो xचर को हल करने में सक्षम नहीं होगा । यह स्थानीय विंडो में दिखाई नहीं देगा, और इसे घड़ी या तत्काल खिड़कियों में देखने की कोशिश करने से संदेश मिलेगा The name 'x' does not exist in the current context:।
— क्रिस्टोफर Currens
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.