वेक्टर इनपुट का परिणाम क्यों होता है। सामान्य () समान इनपुट के साथ 34 बार कॉल करने के बाद बदल जाता है?


10

यहां एक सरल सी # .NET कोर 3.1 प्रोग्राम है जो System.Numerics.Vector2.Normalize()लूप में कॉल करता है (समान कॉल हर कॉल के साथ) और परिणामी सामान्यीकृत ट्यूटोरियल को प्रिंट करता है।

using System;
using System.Numerics;
using System.Threading;

namespace NormalizeTest
{
    class Program
    {
        static void Main()
        {
            Vector2 v = new Vector2(9.856331f, -2.2437377f);
            for(int i = 0; ; i++)
            {
                Test(v, i);
                Thread.Sleep(100);
            }
        }

        static void Test(Vector2 v, int i)
        {
            v = Vector2.Normalize(v);
            Console.WriteLine($"{i:0000}: {v}");
        }
    }
}

और मेरे कंप्यूटर पर उस प्रोग्राम को चलाने का आउटपुट है (संक्षिप्तता के लिए संक्षिप्त):

0000: <0.9750545, -0.22196561>
0001: <0.9750545, -0.22196561>
0002: <0.9750545, -0.22196561>
...
0031: <0.9750545, -0.22196561>
0032: <0.9750545, -0.22196561>
0033: <0.9750545, -0.22196561>
0034: <0.97505456, -0.22196563>
0035: <0.97505456, -0.22196563>
0036: <0.97505456, -0.22196563>
...

तो मेरा प्रश्न यह है कि 34 बार कॉल करने के बाद कॉलिंग का परिणाम क्यों Vector2.Normalize(v)बदलता है ? क्या यह अपेक्षित है, या यह भाषा / क्रम में एक बग है?<0.9750545, -0.22196561><0.97505456, -0.22196563>


फ़्लोट्स अजीब हैं
मिलनी डिक

2
@Milney हो सकता है, लेकिन वे भी निर्धारक हैं । इस व्यवहार को केवल तैरते हुए अजीब नहीं बताया गया है।
कोनराड रुडोल्फ

जवाबों:


14

तो मेरा सवाल यह है कि 34 बार कॉल करने के बाद, वेक्टर 0.. सामान्य (v) कॉलिंग का परिणाम <0.9750545, -0.22196561> से <0.97505456, -0.22196563 तक क्यों बदल जाता है?

तो पहला - परिवर्तन क्यों होता है। परिवर्तित मनाया जाता है क्योंकि कोड जो उन मानों की गणना करता है वे भी बदल जाते हैं।

यदि हम कोड के पहले निष्पादन में WinDbg पर जल्दी टूट जाते हैं और कोड में थोड़ा नीचे जाते हैं जो कि Normalizeएड वेक्टर की गणना करता है , तो हम निम्नलिखित असेंबली को देख सकते हैं (अधिक या कम - मैंने कुछ हिस्सों को काट दिया है):

movss   xmm0,dword ptr [rax]
movss   xmm1,dword ptr [rax+4]
lea     rax,[rsp+40h]
movss   xmm2,dword ptr [rax]
movss   xmm3,dword ptr [rax+4]
mulss   xmm0,xmm2
mulss   xmm1,xmm3
addss   xmm0,xmm1
sqrtss  xmm0,xmm0
lea     rax,[rsp+40h]
movss   xmm1,dword ptr [rax]
movss   xmm2,dword ptr [rax+4]
xorps   xmm3,xmm3
movss   dword ptr [rsp+28h],xmm3
movss   dword ptr [rsp+2Ch],xmm3
divss   xmm1,xmm0
movss   dword ptr [rsp+28h],xmm1
divss   xmm2,xmm0
movss   dword ptr [rsp+2Ch],xmm2
mov     rax,qword ptr [rsp+28h]

और ~ 30 निष्पादन के बाद (इस संख्या के बारे में बाद में) यह कोड होगा:

vmovsd  xmm0,qword ptr [rsp+70h]
vmovsd  qword ptr [rsp+48h],xmm0
vmovsd  xmm0,qword ptr [rsp+48h]
vmovsd  xmm1,qword ptr [rsp+48h]
vdpps   xmm0,xmm0,xmm1,0F1h
vsqrtss xmm0,xmm0,xmm0
vinsertps xmm0,xmm0,xmm0,0Eh
vshufps xmm0,xmm0,xmm0,50h
vmovsd  qword ptr [rsp+40h],xmm0
vmovsd  xmm0,qword ptr [rsp+48h]
vmovsd  xmm1,qword ptr [rsp+40h]
vdivps  xmm0,xmm0,xmm1
vpslldq xmm0,xmm0,8
vpsrldq xmm0,xmm0,8
vmovq   rcx,xmm0

अलग-अलग ऑपकोड, अलग-अलग एक्सटेंशन - एसएसई बनाम एवीएक्स और, मुझे लगता है कि विभिन्न ऑपकोड के साथ हमें गणना की अलग-अलग सटीकता मिलती है।

तो अब इसके बारे में और क्यों? .NET कोर (संस्करण के बारे में निश्चित नहीं है - 3.0 मान - लेकिन यह 2.1 में परीक्षण किया गया था) में कुछ ऐसा है जिसे "Tiered JIT संकलन" कहा जाता है। यह शुरुआत में ऐसा कोड बनाता है जो तेजी से उत्पन्न होता है, लेकिन यह सुपर इष्टतम नहीं हो सकता है। बाद में जब रनटाइम का पता चलता है कि कोड अत्यधिक उपयोग किया जाता है तो यह नया, अधिक अनुकूलित कोड उत्पन्न करने के लिए कुछ अतिरिक्त समय बिताएगा। यह .NET कोर में एक नई बात है इसलिए ऐसा व्यवहार पहले नहीं देखा जा सकता है।

इसके अलावा 34 कॉल क्यों? यह थोड़ा अजीब है क्योंकि मैं यह उम्मीद करूंगा कि यह लगभग 30 निष्पादन के साथ हो सकता है क्योंकि यह वह सीमा है जिस पर टियर कंपाइल किक करता हैकॉरलेर के स्रोत कोड में निरंतर देखा जा सकता है । हो सकता है कि इसमें किक करने पर कुछ अतिरिक्त परिवर्तनशीलता हो।

बस यह पुष्टि करने के लिए कि यह मामला है, आप जारी करने set COMPlus_TieredCompilation=0और फिर से निष्पादन की जाँच करके पर्यावरण चर सेट करके tiered संकलन को अक्षम कर सकते हैं । विचित्र प्रभाव पड़ा है।

C:\Users\lukas\source\repos\FloatMultiple\FloatMultiple\bin\Release\netcoreapp3.1
λ FloatMultiple.exe

0000: <0,9750545  -0,22196561>
0001: <0,9750545  -0,22196561>
0002: <0,9750545  -0,22196561>
...
0032: <0,9750545  -0,22196561>
0033: <0,9750545  -0,22196561>
0034: <0,9750545  -0,22196561>
0035: <0,97505456  -0,22196563>
0036: <0,97505456  -0,22196563>
^C
C:\Users\lukas\source\repos\FloatMultiple\FloatMultiple\bin\Release\netcoreapp3.1
λ set COMPlus_TieredCompilation=0

C:\Users\lukas\source\repos\FloatMultiple\FloatMultiple\bin\Release\netcoreapp3.1
λ FloatMultiple.exe

0000: <0,97505456  -0,22196563>
0001: <0,97505456  -0,22196563>
0002: <0,97505456  -0,22196563>
...
0032: <0,97505456  -0,22196563>
0033: <0,97505456  -0,22196563>
0034: <0,97505456  -0,22196563>
0035: <0,97505456  -0,22196563>
0036: <0,97505456  -0,22196563>

क्या यह अपेक्षित है, या यह भाषा / क्रम में एक बग है?

इसके लिए पहले से ही एक बग बताया गया है - अंक 1119


उनके पास ऐसा कोई सुराग नहीं है जो इसका कारण बनता है। उम्मीद है कि ओपी आपके जवाब के लिए लिंक अप और पोस्ट कर सकता है।
हंस पसंत

1
पूरी तरह से और जानकारीपूर्ण उत्तर के लिए धन्यवाद! वह बग रिपोर्ट वास्तव में मेरी रिपोर्ट है जिसे मैंने इस प्रश्न को पोस्ट करने के बाद दायर किया था, यह नहीं जानते हुए कि यह वास्तव में बग था या नहीं। लगता है जैसे वे बदलते मूल्य को अवांछित व्यवहार मानते हैं जिसके परिणामस्वरूप हाइजेनबग्स और कुछ ऐसा हो सकता है जिसे ठीक किया जाना चाहिए।
वॉल्ट डी

हाँ, मुझे 2 बजे विश्लेषण करने से पहले रेपो की जाँच करनी चाहिए थी :) वैसे भी इस पर गौर करना दिलचस्प समस्या थी।
पावेल asukasik

@HansPassant क्षमा करें, मुझे यकीन नहीं है कि आप मुझे क्या सुझाव दे रहे हैं। क्या आप स्पष्ट कर सकते हैं?
वॉल्ट डी

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