c++ - 10x से 0.1f से 0 धीमा प्रदर्शन क्यों बदल रहा है?




performance visual-studio-2010 (3)

कोड का यह बिट क्यों है,

const float x[16] = {  1.1,   1.2,   1.3,     1.4,   1.5,   1.6,   1.7,   1.8,
                       1.9,   2.0,   2.1,     2.2,   2.3,   2.4,   2.5,   2.6};
const float z[16] = {1.123, 1.234, 1.345, 156.467, 1.578, 1.689, 1.790, 1.812,
                     1.923, 2.034, 2.145,   2.256, 2.367, 2.478, 2.589, 2.690};
float y[16];
for (int i = 0; i < 16; i++)
{
    y[i] = x[i];
}

for (int j = 0; j < 9000000; j++)
{
    for (int i = 0; i < 16; i++)
    {
        y[i] *= x[i];
        y[i] /= z[i];
        y[i] = y[i] + 0.1f; // <--
        y[i] = y[i] - 0.1f; // <--
    }
}

निम्नलिखित बिट से 10 गुना तेजी से चलाएं (जहां कहीं भी ध्यान दिया गया है)?

const float x[16] = {  1.1,   1.2,   1.3,     1.4,   1.5,   1.6,   1.7,   1.8,
                       1.9,   2.0,   2.1,     2.2,   2.3,   2.4,   2.5,   2.6};
const float z[16] = {1.123, 1.234, 1.345, 156.467, 1.578, 1.689, 1.790, 1.812,
                     1.923, 2.034, 2.145,   2.256, 2.367, 2.478, 2.589, 2.690};
float y[16];
for (int i = 0; i < 16; i++)
{
    y[i] = x[i];
}

for (int j = 0; j < 9000000; j++)
{
    for (int i = 0; i < 16; i++)
    {
        y[i] *= x[i];
        y[i] /= z[i];
        y[i] = y[i] + 0; // <--
        y[i] = y[i] - 0; // <--
    }
}

विजुअल स्टूडियो 2010 एसपी 1 के साथ संकलन करते समय। (मैंने अन्य कंपाइलर्स के साथ परीक्षण नहीं किया है।)


जीसीसी में आप एफटीजेड और डीएजेड को इसके साथ सक्षम कर सकते हैं:

#include <xmmintrin.h>

#define FTZ 1
#define DAZ 1   

void enableFtzDaz()
{
    int mxcsr = _mm_getcsr ();

    if (FTZ) {
            mxcsr |= (1<<15) | (1<<11);
    }

    if (DAZ) {
            mxcsr |= (1<<6);
    }

    _mm_setcsr (mxcsr);
}

जीसीसी स्विच का भी उपयोग करें: -msse-mfpmath = sse

(कार्ल हेदरिंगटन के लिए संबंधित क्रेडिट [1])

[1] 1


यह denormalized फ्लोटिंग-बिंदु उपयोग के कारण है। इसे और प्रदर्शन दंड दोनों से छुटकारा पाने के लिए कैसे? असामान्य संख्याओं को मारने के तरीकों के लिए इंटरनेट को खराब करने के कारण, ऐसा लगता है कि अभी तक ऐसा करने का कोई "सर्वश्रेष्ठ" तरीका नहीं है। मुझे इन तीन तरीकों को मिला है जो विभिन्न वातावरणों में सबसे अच्छा काम कर सकते हैं:

  • कुछ जीसीसी वातावरण में काम नहीं कर सकते हैं:

    // Requires #include <fenv.h>
    fesetenv(FE_DFL_DISABLE_SSE_DENORMS_ENV);
    
  • कुछ विजुअल स्टूडियो वातावरण में काम नहीं कर सकता: 1

    // Requires #include <xmmintrin.h>
    _mm_setcsr( _mm_getcsr() | (1<<15) | (1<<6) );
    // Does both FTZ and DAZ bits. You can also use just hex value 0x8040 to do both.
    // You might also want to use the underflow mask (1<<11)
    
  • जीसीसी और विजुअल स्टूडियो दोनों में काम करने के लिए प्रकट होता है:

    // Requires #include <xmmintrin.h>
    // Requires #include <pmmintrin.h>
    _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
    _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
    
  • इंटेल कंपाइलर में आधुनिक इंटेल CPU पर डिफ़ॉल्ट रूप से denormals को अक्षम करने के विकल्प हैं। यहां अधिक जानकारी

  • कंपाइलर स्विच। -ffast-math , -msse या -mfpmath=sse denormals को अक्षम कर देगा और कुछ अन्य चीजें तेज़ी से कर देगा, लेकिन दुर्भाग्यवश अन्य कई अनुमान भी हैं जो आपके कोड को तोड़ सकते हैं। ध्यान से परीक्षण करें! विजुअल स्टूडियो कंपाइलर के लिए फास्ट-मैथ के समतुल्य /fp:fast लेकिन मैं यह पुष्टि करने में सक्षम नहीं हूं कि यह denormals को अक्षम करता है या नहीं। 1


Denormalized फ्लोटिंग बिंदु की दुनिया में आपका स्वागत है! वे प्रदर्शन पर कहर बरबाद कर सकते हैं !!!

फ्लोरेटिंग पॉइंट प्रस्तुति से शून्य के करीब कुछ अतिरिक्त मूल्य प्राप्त करने के लिए असामान्य (या असामान्य) संख्याएं हैंक की तरह हैं। Denormalized फ्लोटिंग-पॉइंट पर संचालन सामान्यीकृत फ्लोटिंग-पॉइंट की तुलना में सैकड़ों गुना धीमा हो सकता है। ऐसा इसलिए है क्योंकि कई प्रोसेसर उन्हें सीधे संभाल नहीं सकते हैं और उन्हें माइक्रोक्रोड का उपयोग करके जाल और हल करना चाहिए।

यदि आप 10,000 पुनरावृत्तियों के बाद संख्याओं को प्रिंट करते हैं, तो आप देखेंगे कि 0 या 0.1 का उपयोग करने के आधार पर वे अलग-अलग मानों में एकत्र हुए हैं।

X64 पर संकलित परीक्षण कोड यहां दिया गया है:

int main() {

    double start = omp_get_wtime();

    const float x[16]={1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9,2.0,2.1,2.2,2.3,2.4,2.5,2.6};
    const float z[16]={1.123,1.234,1.345,156.467,1.578,1.689,1.790,1.812,1.923,2.034,2.145,2.256,2.367,2.478,2.589,2.690};
    float y[16];
    for(int i=0;i<16;i++)
    {
        y[i]=x[i];
    }
    for(int j=0;j<9000000;j++)
    {
        for(int i=0;i<16;i++)
        {
            y[i]*=x[i];
            y[i]/=z[i];
#ifdef FLOATING
            y[i]=y[i]+0.1f;
            y[i]=y[i]-0.1f;
#else
            y[i]=y[i]+0;
            y[i]=y[i]-0;
#endif

            if (j > 10000)
                cout << y[i] << "  ";
        }
        if (j > 10000)
            cout << endl;
    }

    double end = omp_get_wtime();
    cout << end - start << endl;

    system("pause");
    return 0;
}

आउटपुट:

#define FLOATING
1.78814e-007  1.3411e-007  1.04308e-007  0  7.45058e-008  6.70552e-008  6.70552e-008  5.58794e-007  3.05474e-007  2.16067e-007  1.71363e-007  1.49012e-007  1.2666e-007  1.11759e-007  1.04308e-007  1.04308e-007
1.78814e-007  1.3411e-007  1.04308e-007  0  7.45058e-008  6.70552e-008  6.70552e-008  5.58794e-007  3.05474e-007  2.16067e-007  1.71363e-007  1.49012e-007  1.2666e-007  1.11759e-007  1.04308e-007  1.04308e-007

//#define FLOATING
6.30584e-044  3.92364e-044  3.08286e-044  0  1.82169e-044  1.54143e-044  2.10195e-044  2.46842e-029  7.56701e-044  4.06377e-044  3.92364e-044  3.22299e-044  3.08286e-044  2.66247e-044  2.66247e-044  2.24208e-044
6.30584e-044  3.92364e-044  3.08286e-044  0  1.82169e-044  1.54143e-044  2.10195e-044  2.45208e-029  7.56701e-044  4.06377e-044  3.92364e-044  3.22299e-044  3.08286e-044  2.66247e-044  2.66247e-044  2.24208e-044

ध्यान दें कि दूसरी दौड़ में संख्या शून्य के बहुत करीब हैं।

Denormalized संख्या आम तौर पर दुर्लभ हैं और इस प्रकार अधिकांश प्रोसेसर कुशलतापूर्वक उन्हें संभालने की कोशिश नहीं करते हैं।

यह दिखाने के लिए कि इसमें सब कुछ है जो denormalized संख्याओं के साथ करने के लिए है, अगर हम कोड की शुरुआत में इसे जोड़कर शून्य पर denormals फ्लश :

_MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);

फिर 0 साथ संस्करण अब 10x धीमा नहीं है और वास्तव में तेज़ी से हो जाता है। (यह आवश्यक है कि कोड एसएसई सक्षम के साथ संकलित किया जाए।)

इसका मतलब है कि इन अजीब निचले परिशुद्धता लगभग-शून्य मानों का उपयोग करने के बजाय, हम इसके बजाय शून्य पर गोल करते हैं।

समय: कोर i7 920 @ 3.5 गीगाहर्ट्ज:

//  Don't flush denormals to zero.
0.1f: 0.564067
0   : 26.7669

//  Flush denormals to zero.
0.1f: 0.587117
0   : 0.341406

अंत में, इसका वास्तव में कुछ भी नहीं है कि यह एक पूर्णांक या फ़्लोटिंग-पॉइंट है या नहीं। 0 या 0.1f दोनों लूप के बाहर एक रजिस्टर में परिवर्तित / संग्रहीत किया जाता है। तो इसका प्रदर्शन पर कोई प्रभाव नहीं पड़ता है।





floating-point