32 और 64 बिट के लिए संकलन करते समय विशाल प्रदर्शन अंतर (26x तेज)


80

मैं मूल्य प्रकारों और संदर्भ प्रकारों की सूचियों तक पहुँचने के दौरान forऔर ए का उपयोग करने के अंतर को मापने की कोशिश कर रहा था foreach

मैंने प्रोफाइलिंग करने के लिए निम्न वर्ग का उपयोग किया।

public static class Benchmarker
{
    public static void Profile(string description, int iterations, Action func)
    {
        Console.Write(description);

        // Warm up
        func();

        Stopwatch watch = new Stopwatch();

        // Clean up
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();

        watch.Start();
        for (int i = 0; i < iterations; i++)
        {
            func();
        }
        watch.Stop();

        Console.WriteLine(" average time: {0} ms", watch.Elapsed.TotalMilliseconds / iterations);
    }
}

मैंने doubleअपने मूल्य प्रकार के लिए उपयोग किया । और मैंने इस 'नकली वर्ग' को संदर्भ प्रकारों का परीक्षण करने के लिए बनाया है:

class DoubleWrapper
{
    public double Value { get; set; }

    public DoubleWrapper(double value)
    {
        Value = value;
    }
}

अंत में मैंने इस कोड को चलाया और समय के अंतर की तुलना की।

static void Main(string[] args)
{
    int size = 1000000;
    int iterationCount = 100;

    var valueList = new List<double>(size);
    for (int i = 0; i < size; i++) 
        valueList.Add(i);

    var refList = new List<DoubleWrapper>(size);
    for (int i = 0; i < size; i++) 
        refList.Add(new DoubleWrapper(i));

    double dummy;

    Benchmarker.Profile("valueList for: ", iterationCount, () =>
    {
        double result = 0;
        for (int i = 0; i < valueList.Count; i++)
        {
             unchecked
             {
                 var temp = valueList[i];
                 result *= temp;
                 result += temp;
                 result /= temp;
                 result -= temp;
             }
        }
        dummy = result;
    });

    Benchmarker.Profile("valueList foreach: ", iterationCount, () =>
    {
        double result = 0;
        foreach (var v in valueList)
        {
            var temp = v;
            result *= temp;
            result += temp;
            result /= temp;
            result -= temp;
        }
        dummy = result;
    });

    Benchmarker.Profile("refList for: ", iterationCount, () =>
    {
        double result = 0;
        for (int i = 0; i < refList.Count; i++)
        {
            unchecked
            {
                var temp = refList[i].Value;
                result *= temp;
                result += temp;
                result /= temp;
                result -= temp;
            }
        }
        dummy = result;
    });

    Benchmarker.Profile("refList foreach: ", iterationCount, () =>
    {
        double result = 0;
        foreach (var v in refList)
        {
            unchecked
            {
                var temp = v.Value;
                result *= temp;
                result += temp;
                result /= temp;
                result -= temp;
            }
        }

        dummy = result;
    });

    SafeExit();
}

मैंने चयन किया Releaseऔर Any CPUविकल्प दिए, कार्यक्रम चलाया और निम्नलिखित समय मिला:

valueList for:  average time: 483,967938 ms
valueList foreach:  average time: 477,873079 ms
refList for:  average time: 490,524197 ms
refList foreach:  average time: 485,659557 ms
Done!

फिर मैंने रिलीज़ और x64 विकल्प चुने, कार्यक्रम चलाया और निम्नलिखित बार मिला:

valueList for:  average time: 16,720209 ms
valueList foreach:  average time: 15,953483 ms
refList for:  average time: 19,381077 ms
refList foreach:  average time: 18,636781 ms
Done!

X64 बिट संस्करण इतना तेज क्यों है? मुझे कुछ अंतर की उम्मीद थी, लेकिन इस बड़े से कुछ नहीं।

मेरे पास अन्य कंप्यूटरों तक पहुंच नहीं है। क्या आप कृपया इसे अपनी मशीनों पर चला सकते हैं और मुझे परिणाम बता सकते हैं? मैं विजुअल स्टूडियो 2015 का उपयोग कर रहा हूं और मेरे पास इंटेल कोर i7 930 है।

यहाँ SafeExit()विधि है, ताकि आप स्वयं को संकलित / चला सकें:

private static void SafeExit()
{
    Console.WriteLine("Done!");
    Console.ReadLine();
    System.Environment.Exit(1);
}

अनुरोध के अनुसार, double?मेरे बजाय का उपयोग कर DoubleWrapper:

कोई सीपीयू

valueList for:  average time: 482,98116 ms
valueList foreach:  average time: 478,837701 ms
refList for:  average time: 491,075915 ms
refList foreach:  average time: 483,206072 ms
Done!

64

valueList for:  average time: 16,393947 ms
valueList foreach:  average time: 15,87007 ms
refList for:  average time: 18,267736 ms
refList foreach:  average time: 16,496038 ms
Done!

अंतिम लेकिन कम से कम नहीं: एक x86प्रोफ़ाइल बनाने से मुझे उपयोग करने के लगभग समान परिणाम मिलते हैंAny CPU


14
"एनी सीपीयू"! = "32 बिट्स"! यदि "किसी भी सीपीयू" को संकलित किया जाता है, तो आपका आवेदन आपके 64 बिट सिस्टम पर 64 बिट प्रक्रिया के रूप में चलना चाहिए। इसके अलावा, मैं GC के साथ मेसिंग कोड हटा दूंगा। यह वास्तव में मदद नहीं करता है।
थोरस्टेन डिटमार

9
@ThorstenDittmar जीसी कॉल माप किए गए कोड के बजाय माप से पहले हैं। जीसी टाइमिंग की किस्मत इस तरह के माप को प्रभावित कर सकती है। इसके अलावा, बिल्ड के बीच एक कारक के रूप में "एहसान 32-बिट" बनाम "पक्ष 64-बिट" है।
जॉन हैना

1
@ThorstenDittmar लेकिन मैं रिलीज़ संस्करण (विजुअल स्टूडियो के बाहर) चलाता हूं और टास्क मैनेजर का कहना है कि यह एक 32 बिट एप्लिकेशन है (जब किसी भी सीपीयू को संकलित किया जाता है)। इसके अलावा। जैसा कि जॉन हैना ने कहा, जीसी कॉल उपयोगी है।
ट्रूअर

2
आप किस रनटाइम संस्करण का उपयोग कर रहे हैं? 4.6 में नया RyuJIT बहुत तेज है, लेकिन पहले के संस्करणों के लिए भी, x64 संकलक और JITer नए और x32 संस्करणों की तुलना में अधिक उन्नत थे। वे x86 संस्करणों की तुलना में कहीं अधिक आक्रामक अनुकूलन करने में सक्षम हैं।
पनगीओटीस कानावोस

2
मैं ध्यान देता हूं कि इसमें शामिल प्रकार का कोई प्रभाव नहीं है; बदलने के doubleलिए float, longया intआप समान परिणाम प्राप्त करते हैं।
जॉन हैना

जवाबों:


87

मैं इसे 4.5.2 पर पुन: पेश कर सकता हूं। यहाँ कोई RyuJIT नहीं। दोनों x86 और x64 disassemblies उचित दिखते हैं। रेंज की जांच वगैरह समान हैं। वही बुनियादी ढाँचा। कोई लूप नहीं है।

x86 फ्लोट निर्देशों के एक अलग सेट का उपयोग करता है। इन निर्देशों का प्रदर्शन विभाजन के अलावा x64 निर्देशों के साथ तुलनीय प्रतीत होता है :

  1. 32 बिट x87 फ्लोट निर्देश 10 बाइट सटीक रूप से आंतरिक रूप से उपयोग करते हैं।
  2. विस्तारित सटीक विभाजन सुपर धीमा है।

विभाजन ऑपरेशन 32 बिट संस्करण को बहुत धीमा बनाता है। विभाजन को रद्द करना एक बड़ी डिग्री (430 बिट से 3.25ms से 32 बिट नीचे) के प्रदर्शन को बराबर करता है।

पीटर कॉर्डेस बताते हैं कि दो फ्लोटिंग पॉइंट यूनिट्स के इंस्ट्रक्शन लेटेस उस डिसिमिलर नहीं हैं। हो सकता है कि इंटरमीडिएट के कुछ रिजल्ट्स में नंबरों की संख्या हो या NaN। इन इकाइयों में से एक में धीमी गति से ट्रिगर हो सकता है। या, शायद मान दो कार्यान्वयन के बीच 10 बाइट बनाम 8 बाइट फ्लोट परिशुद्धता के कारण अलग हो जाते हैं।

पीटर कॉर्डेस यह भी बताते हैं कि सभी मध्यवर्ती परिणाम NaN हैं ... इस समस्या को दूर करना ( valueList.Add(i + 1)ताकि कोई विभाजक शून्य न हो) ज्यादातर परिणामों को बराबर करता है। जाहिर है, 32 बिट कोड NaN ऑपरेंड्स को बिल्कुल पसंद नहीं करता है। चलो कुछ मध्यवर्ती मूल्यों को मुद्रित करते हैं: if (i % 1000 == 0) Console.WriteLine(result);। यह पुष्टि करता है कि डेटा अब समझदार है।

बेंचमार्किंग करते समय आपको एक यथार्थवादी कार्यभार को निर्धारित करना होगा। लेकिन किसने सोचा होगा कि एक निर्दोष विभाजन आपके बेंचमार्क को गड़बड़ कर सकता है ?!

बेहतर बेंचमार्क पाने के लिए बस संख्याओं को समेटने की कोशिश करें।

डिवीजन और मोडुलो हमेशा बहुत धीमे होते हैं। यदि आप बीसीएल Dictionaryकोड को संशोधित करते हैं तो बस बाल्टी इंडेक्स परफॉर्मेंस को मापने के लिए मॉडुलो ऑपरेटर का उपयोग न करें। यह कितना धीमा विभाजन है।

यहाँ 32 बिट कोड है:

यहाँ छवि विवरण दर्ज करें

64 बिट कोड (समान संरचना, तेज विभाजन):

यहाँ छवि विवरण दर्ज करें

यह SSE निर्देशों के उपयोग के बावजूद सदिश नहीं है।


11
"किसने सोचा होगा कि एक निर्दोष विभाजन आपके बेंचमार्क को गड़बड़ कर सकता है?" मैंने तुरंत किया, जैसे ही मैंने आंतरिक पाश में एक विभाजन देखा, esp। निर्भरता श्रृंखला के हिस्से के रूप में। डिवीजन केवल निर्दोष है जब 2. की शक्ति से पूर्णांक विभाजन होता है। agner.org/optimize insn tables से: Nehalem 7-27 fdivचक्र विलंबता (और समान पारस्परिक प्रवाह) है। divsd7-22 चक्र है। addsd3 सी विलंबता पर, 1 / सी थ्रूपुट। डिवीजन इंटेल / एएमडी सीपीयू में केवल गैर-पाइपलाइज्ड निष्पादन इकाई है। C # JIT लूप को x86-64 (के साथ divPd) के लिए वेक्टर नहीं कर रहा है ।
पीटर कॉर्ड्स

1
इसके अलावा, क्या 32b C # का SSE गणित का उपयोग नहीं करना सामान्य है? क्या JIT के बिंदु के वर्तमान मशीन भाग की सुविधाओं का उपयोग नहीं किया जा रहा है? हसवेल पर और बाद में, यह केवल SSE के बजाय 256b AVX2 के साथ पूर्णांक लूप को ऑटो-वेक्टर कर सकता है। एफपी लूप के वैश्वीकरण को प्राप्त करने के लिए, मुझे लगता है कि आपको उन्हें समानांतर में 4 संचायक जैसे सामान के साथ लिखना होगा, क्योंकि एफपी गणित सहयोगी नहीं है। लेकिन वैसे भी, 32 बिट मोड में SSE का उपयोग करना अधिक तेज़ है, क्योंकि आपके पास उसी स्केलर का काम करने के लिए कम निर्देश हैं जब आपको x87 FP स्टैक को जॉगल नहीं करना पड़ता है।
पीटर कॉर्ड्स

4
वैसे भी, div बहुत धीमा है, लेकिन 10B x87 fdiv 8B SSE2 की तुलना में बहुत धीमा नहीं है, इसलिए यह x86 और x86-64 के बीच अंतर को स्पष्ट नहीं करता है। क्या यह समझा सकता है कि यह FPU अपवाद है या denormals / infinities के साथ मंदी है। X87 FPU नियंत्रण शब्द SSE गोलाई / अपवाद नियंत्रण रजिस्टर ( MXCSR) से अलग है। Denormals या NaNs के विभिन्न हैंडलिंग मुझे लगता है कि 26 पूर्ण भिन्नता के कारक की व्याख्या कर सकते हैं। MXCSR में C # मान को निर्धारित कर सकता है।
पीटर कॉर्ड्स

2
@Trauer और usr: मैंने अभी देखा कि valueList[i] = i, से शुरू i=0, इसलिए पहला लूप पुनरावृत्ति करता है 0.0 / 0.0। तो आपके पूरे बेंचमार्क में हर ऑपरेशन NaNs के साथ किया जाता है । वह विभाजन कम और निर्दोष दिख रहा है! मैं इसके साथ प्रदर्शन पर विशेषज्ञ नहीं हूं NaN, या इसके लिए x87 और SSE के बीच का अंतर है, लेकिन मुझे लगता है कि यह 26x पूर्ण अंतर बताता है। मुझे यकीन है कि अगर आप इनिशियलाइज़ करते हैं तो आपके परिणाम 32 और 64 बिट के बीच बहुत करीब होंगे valueList[i] = i+1
पीटर कॉर्ड्स

1
फ्लश-टू-ज़ीरो के लिए, मैं 64-बिट डबल के साथ इसके लिए उत्सुक नहीं हूं, लेकिन जब 80 बिट विस्तारित और 64-बिट डबल का एक साथ उपयोग किया जाता है, तो ऐसी परिस्थितियां जहां 80-बिट मान कम हो सकता है और फिर पर्याप्त रूप से बढ़ सकता है। एक मूल्य प्राप्त करने के लिए जो 64-बिट के रूप में प्रतिनिधित्व करने योग्य doubleहोगा, बहुत दुर्लभ होगा। 80-बिट प्रकार के मुख्य उपयोग पैटर्न में से एक था, बहुत से अंत तक परिणामों को कसकर गोल किए बिना कई संख्याओं को एक साथ अभिव्यक्त करने की अनुमति देना। उस पैटर्न के तहत, ओवरफ्लो केवल एक समस्या नहीं है।
सुपरकैट

31

valueList[i] = i, से शुरू i=0, तो पहला लूप पुनरावृत्ति करता है 0.0 / 0.0तो आपके पूरे बेंचमार्क में हर ऑपरेशन NaNs के साथ किया जाता है ।

जैसा कि @usr ने disassembly आउटपुट में दिखाया , 32 बिट संस्करण में x87 फ्लोटिंग पॉइंट का उपयोग किया गया, जबकि 64 बिट में SSE फ़्लोटिंग पॉइंट का उपयोग किया गया।

मैं इसके साथ प्रदर्शन पर विशेषज्ञ नहीं हूं NaN, या इसके लिए x87 और SSE के बीच का अंतर है, लेकिन मुझे लगता है कि यह 26x पूर्ण अंतर बताता है। मुझे यकीन है कि अगर आप इनिशियलाइज़ करते हैं तो आपके परिणाम 32 और 64 बिट के बीच बहुत करीब होंगे valueList[i] = i+1। (अपडेट: usr ने पुष्टि की कि इसने 32 और 64 बिट के प्रदर्शन को काफी करीब बना दिया है।)

अन्य ऑपरेशन की तुलना में डिवीजन बहुत धीमा है। @ Usr के उत्तर पर मेरी टिप्पणियाँ देखें। इसके अलावा हार्डवेयर के बारे में महान सामान के टन के लिए http://agner.org/optimize/ , और asm और C / C ++ को अनुकूलित करते हुए, इसमें से कुछ C # के लिए प्रासंगिक है। उनके पास हाल ही के x86 सीपीयू के लिए अधिकांश निर्देशों के लिए विलंबता और थ्रूपुट के निर्देश तालिकाएं हैं।

हालांकि, 10B x87 सामान्य मूल्यों के लिए, fdivSSE2 की 8B डबल परिशुद्धता की तुलना में बहुत धीमा नहीं है divsd। NaN, infinities, या denormals के साथ पूर्ण अंतर के बारे में IDK।

हालांकि NaNs और अन्य FPU अपवादों के साथ क्या होता है, इसके लिए उनके अलग-अलग नियंत्रण हैं। X87 एफपीयू नियंत्रण शब्द SSE राउंडिंग / अपवाद नियंत्रण रजिस्टर (MXCSR) से अलग है। यदि x87 को हर डिवीजन के लिए CPU अपवाद मिल रहा है, लेकिन SSE नहीं है, तो वह आसानी से 26 का कारक बताता है। या शायद NaNs को हैंडल करते समय केवल एक प्रदर्शन अंतर होता है। हार्डवेयर के बाद के माध्यम से मंथन के लिए अनुकूलित नहीं हैNaNNaN

IDK अगर denormals साथ मंदी से बचने के लिए SSE नियंत्रण खेलने में यहाँ आ जाएगा, के बाद से मेरा मानना है कि resultहो सकता है NaNहर समय। IDK अगर C # MXCSR में denormals-are-zero झंडा, या flush-to-zero-flag (जो पहले पढ़े गए शून्य को वापस पढ़ने के बजाय शून्य के रूप में लिखता है) सेट करता है।

मुझे SSE फ्लोटिंग पॉइंट कंट्रोल के बारे में एक इंटेल आर्टिकल मिला , जो x87 FPU कंट्रोल शब्द के साथ इसके विपरीत है। NaNहालांकि इसके बारे में कहने के लिए बहुत कुछ नहीं है । यह इसके साथ समाप्त होता है:

निष्कर्ष

Denormals और कम संख्या के कारण क्रमांकन और प्रदर्शन के मुद्दों से बचने के लिए, अस्थायी बिंदु अनुप्रयोगों के लिए उच्चतम प्रदर्शन को सक्षम करने के लिए हार्डवेयर के भीतर Flush-to-Zero और Denormals-Are-Zero मोड सेट करने के लिए SSE और SSE2 निर्देशों का उपयोग करें।

आईडीके अगर यह किसी भी तरह से विभाजित-शून्य के साथ मदद करता है।

बनाम foreach के लिए

यह लूप बॉडी का परीक्षण करने के लिए दिलचस्प हो सकता है जो थ्रूपुट-सीमित है, बल्कि केवल एक ही लूप-आधारित निर्भरता श्रृंखला है। जैसा कि यह है, सभी कार्य पिछले परिणामों पर निर्भर करते हैं; सीपीयू के समानांतर कुछ करने के लिए कुछ नहीं है (अन्य सीमाओं के अलावा अगले सरणी लोड की जांच करते समय mul / div श्रृंखला चल रही है)।

यदि "वास्तविक कार्य" सीपीयू निष्पादन संसाधनों के अधिक कब्जे में है, तो आप तरीकों के बीच अधिक अंतर देख सकते हैं। इसके अलावा, पूर्व-सैंडीब्रिज इंटेल पर, 28uop लूप बफर में लूप फिटिंग के बीच एक बड़ा अंतर है या नहीं। आपको निर्देश डिकोड बाधाओं से मिलता है यदि नहीं, तो। जब औसत निर्देश लंबाई लंबी होती है (जो SSE के साथ होती है)। निर्देश जो एक से अधिक यूओपी को डिकोड करते हैं, वे भी डिकोडर थ्रूपुट को सीमित कर देंगे, जब तक कि वे एक पैटर्न में नहीं आते हैं जो डिकोडर्स के लिए अच्छा है (उदाहरण 2-1-1)। तो लूप ओवरहेड के अधिक निर्देशों के साथ एक लूप 28-एंट्री यूओपी कैश में लूप फिटिंग के बीच अंतर कर सकता है या नहीं, जो नेहलम पर एक बड़ी बात है, और कभी-कभी सैंडब्रिज और बाद में मददगार होता है।


मेरे पास ऐसा कोई मामला नहीं है जहां मैंने NaNs के डेटा स्ट्रीम में होने के आधार पर किसी भी प्रदर्शन अंतर को देखा हो, लेकिन असामान्य संख्याओं की उपस्थिति प्रदर्शन में भारी अंतर ला सकती है । इस उदाहरण में ऐसा प्रतीत नहीं होता है, लेकिन यह ध्यान में रखना है।
जेसन आर

@ जेसन: क्या सिर्फ इसलिए कि NaNव्यवहार में दुर्लभ हैं? मैंने सभी सामग्रियों को डीनॉर्मल के बारे में छोड़ दिया, और इंटेल के सामान के लिए लिंक, ज्यादातर पाठकों के लाभ के लिए, इसलिए नहीं कि मुझे लगा कि इस विशिष्ट मामले पर वास्तव में इसका बहुत प्रभाव पड़ेगा।
पीटर कॉर्ड्स

अधिकांश अनुप्रयोगों में वे दुर्लभ हैं। हालांकि, जब नए सॉफ़्टवेयर का विकास किया जाता है जो फ्लोटिंग पॉइंट का उपयोग करता है, तो यह वांछित परिणाम के बजाय NaNs की स्ट्रीम प्राप्त करने के लिए कार्यान्वयन कीड़े के लिए दुर्लभ नहीं है! यह मेरे साथ कई बार हुआ है और जब NaN पॉप अप होता है तो मुझे कोई ध्यान देने योग्य प्रदर्शन याद नहीं होता है। अगर मैंने ऐसा कुछ किया है जो विपरीत दिखाई देता है, तो मैं इसका पालन करता हूं; आम तौर पर प्रदर्शन में तुरंत ध्यान देने योग्य गिरावट होती है। ध्यान दें कि ये सिर्फ मेरे वास्तविक अनुभव पर आधारित हैं; NaNs के साथ कुछ प्रदर्शन ड्रॉप हो सकते हैं जिन्हें मैंने अभी देखा नहीं है।
जेसन आर

@JasonR: IDK, शायद NaN बहुत ज्यादा नहीं हैं अगर SSE के साथ कोई धीमा हो। स्पष्ट रूप से वे x87 के लिए एक बड़ी समस्या हैं। SSE FP शब्दार्थ इंटेल द्वारा PII / PIII दिनों में डिजाइन किए गए थे। उन सीपीयू में हुड के तहत एक ही आउट-ऑफ-ऑर्डर मशीनरी है, जो वर्तमान डिजाइनों के रूप में है, इसलिए संभवतः एसएसई डिजाइन करते समय उनके पास पी 6 के लिए उच्च प्रदर्शन था। (हां, स्काईलेक P6 माइक्रोआर्किटेक्चर पर आधारित है। कुछ चीजें बदल गई हैं, लेकिन यह अभी भी उफोडे हुए हैं, और उन्हें फिर से ऑर्डर बफर के साथ पोर्ट निष्पादित करने के लिए शेड्यूल करता है।) x87 शब्दार्थ एक वैकल्पिक सह-प्रोसेसर चिप के लिए डिज़ाइन किया गया था। इन-ऑर्डर स्केलर सीपीयू।
पीटर कॉर्ड्स

@PeterCordes Skylake को P6- आधारित चिप कॉल करना बहुत दूर की बात है। 1) एफपीयू सैंडी ब्रिज युग के दौरान पूरी तरह से बदल दिया गया था, इसलिए पुराने पी 6 एफपीयू मूल रूप से आज तक चला गया है; 2) x86 से यूओपी डिकोड में कोर 2 युग के दौरान एक महत्वपूर्ण संशोधन किया गया था: जबकि पिछले डिजाइनों ने अलग-अलग यूओपी के रूप में गणना और मेमोरी निर्देश को डिकोड किया है, जबकि कोर 2 + चिप में एक यूप्यूट इंस्ट्रक्शन और एक मेमोरी ऑपरेटर से मिलकर यूओपी हैं । इसने अधिक जटिल डिजाइन और संभावित रूप से कम शिखर आवृत्ति की कीमत पर प्रदर्शन और शक्ति में काफी सुधार किया।
शोडणशोक

1

हमारे पास यह अवलोकन है कि सभी फ्लोटिंग पॉइंट ऑपरेशंस में से 99.9% में NaN शामिल होगा, जो कम से कम अत्यधिक असामान्य (पहले पीटर कॉर्ड्स द्वारा पाया गया) है। हमारे पास usr का एक और प्रयोग है, जिसमें पाया गया कि डिवीजन निर्देशों को हटाने से समय का अंतर लगभग पूरी तरह से चला जाता है।

हालांकि तथ्य यह है कि NaN केवल इसलिए उत्पन्न होता है क्योंकि बहुत पहले विभाजन 0.0 / 0.0 की गणना करता है जो प्रारंभिक NaN देता है। यदि विभाजन नहीं किए जाते हैं, तो परिणाम हमेशा 0.0 होगा, और हम हमेशा 0.0 * अस्थायी -> 0.0, 0.0 + अस्थायी -> अस्थायी, अस्थायी - अस्थायी = 0.0 की गणना करेंगे। इसलिए विभाजन को हटाने से न केवल विभाजन हटा दिए गए, बल्कि NaN को भी हटा दिया गया। मुझे उम्मीद है कि NaN वास्तव में समस्या है, और यह कि एक कार्यान्वयन NaN की बहुत धीरे-धीरे संभालता है, जबकि दूसरे को समस्या नहीं है।

यह सार्थक होगा कि मैं = 1 पर लूप शुरू करें और फिर से माप करें। चार आपरेशनों का परिणाम है * अस्थायी, + अस्थायी / अस्थायी, - अस्थायी रूप से प्रभावी रूप से (1 - अस्थायी) जोड़ते हैं, इसलिए हमारे पास अधिकांश कार्यों के लिए कोई असामान्य संख्या (0, अनंत, NaN) नहीं होगी।

एकमात्र समस्या यह हो सकती है कि विभाजन हमेशा पूर्णांक परिणाम देता है, और कुछ विभाजन कार्यान्वयन में शॉर्टकट होते हैं जब सही परिणाम कई बिट्स का उपयोग नहीं करता है। उदाहरण के लिए, 310.0 / 31.0 को विभाजित करने पर 10.0 शेष 0.0 ​​के साथ पहले चार बिट्स के रूप में देता है, और कुछ कार्यान्वयन शेष 50 या तो बिट्स का मूल्यांकन करना बंद कर सकते हैं जबकि अन्य नहीं कर सकते। यदि एक महत्वपूर्ण अंतर है, तो परिणाम = 1.0 / 3.0 के साथ लूप शुरू करने से फर्क पड़ेगा।


-2

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

प्रोसेसर वास्तुकला:

इंटेल का सीपीयू आर्किटेक्चर विशुद्ध रूप से 64 बिट था। 32bit कोड निष्पादित करने के लिए, निष्पादन से पहले 64 बिट निर्देशों को बदलने के लिए (सीपीयू के अंदर) 32 बिट निर्देश को बदलने की आवश्यकता है।

एएमडी की सीपीयू वास्तुकला उनके 32 बिट आर्किटेक्चर के शीर्ष पर 64 बिट का सही निर्माण करने के लिए थी; यह है, यह अनिवार्य रूप से 64 बिट सीमाओं के साथ एक 32 बिट वास्तुकला था - कोई कोड रूपांतरण प्रक्रिया नहीं थी।

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

.NET JIT

यह तर्क दिया जाता है कि .NET (और जावा जैसी अन्य प्रबंधित भाषाएं) C ++ जैसी भाषाओं को बेहतर बनाने में सक्षम हैं क्योंकि JIT कंपाइलर आपके प्रोसेसर आर्किटेक्चर के अनुसार आपके कोड को अनुकूलित करने में सक्षम है। इस संबंध में, आप पा सकते हैं कि जेआईटी संकलक 64 बिट आर्किटेक्चर में कुछ का उपयोग कर रहा है जो संभवतः 32 बिट में निष्पादित होने पर उपलब्ध नहीं था या वर्कअराउंड की आवश्यकता नहीं थी।

ध्यान दें:

DoubleWrapper का उपयोग करने के बजाय, क्या आपने Nullable<double>शॉर्टहैंड सिंटैक्स का उपयोग करने पर विचार किया है : double?- मुझे यह देखने में रुचि होगी कि आपके परीक्षणों पर कोई प्रभाव पड़े या नहीं।

नोट 2: कुछ लोग IA-64 के साथ 64 बिट आर्किटेक्चर के बारे में मेरी टिप्पणी को स्वीकार करते दिख रहे हैं। बस स्पष्ट करने के लिए, मेरे उत्तर में, 64 बिट x86-64 को संदर्भित करता है और 32 बिट x86-32 को संदर्भित करता है। यहाँ कुछ भी नहीं संदर्भित IA-64!


4
ठीक है, तो यह 26x तेज क्यों है? उत्तर में यह नहीं मिल सकता है।
usr

2
मैं अनुमान लगा रहा हूं कि यह मतभेद है, लेकिन अनुमान लगाने से ज्यादा नहीं।
जॉन हन्ना

2
@seriesOne: मुझे लगता है कि MSalters यह कहना चाह रहे हैं कि आप I86-64 को x86-64 के साथ मिला रहे हैं। (इंटेल x86-64 के लिए IA-32e का उपयोग उनके नियमावली में भी करता है)। हर किसी का डेस्कटॉप सीपीयू x86-64 है। इटानिक कुछ साल पहले डूब गया था, और मुझे लगता है कि ज्यादातर सर्वर में इस्तेमाल किया गया था, न कि वर्कस्टेशन। Core2 (x86-64 लॉन्ग मोड का समर्थन करने वाला पहला पी 6 परिवार सीपीयू) वास्तव में 64 बिट मोड में कुछ सीमाएं हैं। उदाहरण के लिए यूओपी मैक्रो-फ्यूजन केवल 32 बिट मोड में काम करता है। इंटेल और एएमडी ने एक ही काम किया: अपने 32 बिट डिज़ाइन को 64 बिट तक बढ़ाया।
पीटर कॉर्ड्स

1
@PeterCordes ने IA-64 का उल्लेख कहां किया? मुझे पता है कि इटेनियम सीपीयू एक पूरी तरह से अलग डिजाइन और निर्देश सेट था; ईपीआईसी या स्पष्ट रूप से समानांतर निर्देश कम्प्यूटिंग के रूप में चिह्नित शुरुआती मॉडल। मुझे लगता है कि MSalters 64bit और IA-64 का सामना कर रहा है। मेरा जवाब x86-64 आर्किटेक्चर के लिए सही है
मैथ्यू लेटन

2
@ series0ne: ठीक है, फिर इंटेल सीपीयू के बारे में आपका पैराग्राफ "विशुद्ध रूप से 64 बिट" है पूरी बकवास है। मैंने मान लिया कि आप IA-64 के बारे में सोच रहे थे क्योंकि तब आप पूरी तरह से गलत नहीं होंगे। 32bit कोड चलाने के लिए एक अतिरिक्त अनुवाद कदम नहीं था। X86-> यूओपी डिकोडर्स में दो समान मोड हैं: x86 और x86-64। इंटेल ने P4 के शीर्ष पर 64bit P4 बनाया है। 64bit Core2 कोर और पेंटियम एम पर कई अन्य वास्तु सुधारों के साथ आया था, लेकिन केवल 32 बिट मोड में काम करने वाले मैक्रो-फ्यूजन जैसी चीजें दिखाती हैं कि 64 बिट पर बोल्ट लगाया गया था। (डिजाइन प्रक्रिया में काफी जल्दी, लेकिन अभी भी।)
पीटर कॉर्ड्स
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.