c++ - কেন 0.1f থেকে 0x গতিশীলতা 10x দ্বারা কর্মক্ষমতা পরিবর্তন করে?




performance visual-studio-2010 (4)

এটা ডিমান্ডালাইজড ভাসমান-বিন্দু ব্যবহার কারণে। কিভাবে এটি এবং কর্মক্ষমতা জরিমানা উভয় পরিত্রাণ পেতে? অস্বাভাবিক সংখ্যাকে হত্যা করার উপায়গুলির জন্য ইন্টারনেটকে ভুগছে বলে মনে হচ্ছে, এটি করার জন্য এখনও কোনও "সর্বোত্তম" উপায় নেই। আমি এই তিনটি পদ্ধতি খুঁজে পেয়েছি যা বিভিন্ন পরিবেশে সর্বোত্তম কাজ করতে পারে:

  • কিছু GCC পরিবেশে কাজ করতে পারে না:

    // 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);
  • ইন্টেল কম্পাইলারের ডিফল্টরূপে ডিফল্টরূপে অ্যান্টিনাল ইন্টেল CPUs এ অক্ষম করার বিকল্প রয়েছে। আরো বিস্তারিত এখানে

  • কম্পাইলার সুইচ। -ffast-math , -msse বা -mfpmath=sse denormals নিষ্ক্রিয় করে এবং কিছু অন্যান্য জিনিস দ্রুততর করে তুলবে, কিন্তু দুর্ভাগ্যবশত অন্যান্য -mfpmath=sse ভাঙ্গতে পারে এমন অনেকগুলি আনুমানিক কাজও করে। সাবধানে পরীক্ষা! ভিসুয়াল স্টুডিও কম্পাইলারের জন্য দ্রুত-গণিতের সমতুল্য /fp:fast তবে আমি ডিনামোরগুলিকে অক্ষম করে কিনা তা নিশ্চিত করতে সক্ষম নই। 1

কেন কোড এই বিট,

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 SP1 সঙ্গে কম্পাইল করার সময়। (আমি অন্যান্য কম্পাইলার সঙ্গে পরীক্ষিত না।)


জিকেসি এ আপনি এফটিজেড এবং ড্যাজেড সক্ষম করতে পারবেন:

#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);
}

GCC সুইচ ব্যবহার করুন: -msse -mfpmath = sse

(কার্ল হেইথিংটন [1] এর সাথে সম্পর্কিত ক্রেডিট)

[1] 1


gcc ব্যবহার করে এবং উত্পন্ন সমাবেশে একটি বিভাজক প্রয়োগ শুধুমাত্র এই পার্থক্য ফলন:

73c68,69
<   movss   LCPI1_0(%rip), %xmm1
---
>   movabsq $0, %rcx
>   cvtsi2ssq   %rcx, %xmm1
81d76
<   subss   %xmm1, %xmm0

cvtsi2ssq এক 10 ধীরে ধীরে ধীরে ধীরে।

দৃশ্যত, float সংস্করণটি মেমরি থেকে লোড হওয়া একটি XMM নিবন্ধ ব্যবহার করে, যখন int সংস্করণ cvtsi2ssq নির্দেশ ব্যবহার করে float একটি বাস্তব int মান 0 রূপান্তর করে, এটি অনেক সময় নেয়। Passing -O3 gcc সাহায্য করে না। (gcc সংস্করণ 4.2.1।)

( float পরিবর্তে double ব্যবহার করা কোন ব্যাপার নয়, এটি cvtsi2ssq কে cvtsi2ssq পরিবর্তিত করে।)

হালনাগাদ

কিছু অতিরিক্ত পরীক্ষা দেখায় যে এটি cvtsi2ssq নির্দেশের অগত্যা নয়। একবার মুছে ফেলা হলে (একটি int ai=0;float a=ai; ব্যবহার করে int ai=0;float a=ai; এবং 0 পরিবর্তে ব্যবহার করে), গতির পার্থক্য অবশিষ্ট থাকে। তাই @ মিস্টিসিয়াল সঠিক, ডিমান্ডালাইজড ভাসা পার্থক্য করে। 0 এবং 0.1f মধ্যে মান পরীক্ষা করে দেখা যেতে পারে। উপরের কোডে বাঁকানো বিন্দু আনুমানিক 0.00000000000000000000000000000001 , যখন লুপ হঠাৎ 10 গুণ বেশি সময় নেয়।

আপডেট << 1

এই আকর্ষণীয় ঘটনা একটি ছোট ভিজ্যুয়ালাইজেশন:

  • কলাম 1: একটি ভাসা, প্রতিটি পুনরাবৃত্তি জন্য 2 দ্বারা বিভক্ত
  • কলাম 2: এই ভাসা বাইনারি উপস্থাপনা
  • কলাম 3: এই float 1e7 বার সমষ্টি নেওয়া সময়

আপনি স্পষ্টভাবে এক্সপোনেট (শেষ 9 বিট) এর সর্বনিম্ন মান পরিবর্তন করতে পারেন, যখন denormalization সেট করে। সেই সময়ে, সহজ সংযোজন 20 বার ধীর হয়ে যায়।

0.000000000000000000000000000000000100000004670110: 10111100001101110010000011100000 45 ms
0.000000000000000000000000000000000050000002335055: 10111100001101110010000101100000 43 ms
0.000000000000000000000000000000000025000001167528: 10111100001101110010000001100000 43 ms
0.000000000000000000000000000000000012500000583764: 10111100001101110010000110100000 42 ms
0.000000000000000000000000000000000006250000291882: 10111100001101110010000010100000 48 ms
0.000000000000000000000000000000000003125000145941: 10111100001101110010000100100000 43 ms
0.000000000000000000000000000000000001562500072970: 10111100001101110010000000100000 42 ms
0.000000000000000000000000000000000000781250036485: 10111100001101110010000111000000 42 ms
0.000000000000000000000000000000000000390625018243: 10111100001101110010000011000000 42 ms
0.000000000000000000000000000000000000195312509121: 10111100001101110010000101000000 43 ms
0.000000000000000000000000000000000000097656254561: 10111100001101110010000001000000 42 ms
0.000000000000000000000000000000000000048828127280: 10111100001101110010000110000000 44 ms
0.000000000000000000000000000000000000024414063640: 10111100001101110010000010000000 42 ms
0.000000000000000000000000000000000000012207031820: 10111100001101110010000100000000 42 ms
0.000000000000000000000000000000000000006103515209: 01111000011011100100001000000000 789 ms
0.000000000000000000000000000000000000003051757605: 11110000110111001000010000000000 788 ms
0.000000000000000000000000000000000000001525879503: 00010001101110010000100000000000 788 ms
0.000000000000000000000000000000000000000762939751: 00100011011100100001000000000000 795 ms
0.000000000000000000000000000000000000000381469876: 01000110111001000010000000000000 896 ms
0.000000000000000000000000000000000000000190734938: 10001101110010000100000000000000 813 ms
0.000000000000000000000000000000000000000095366768: 00011011100100001000000000000000 798 ms
0.000000000000000000000000000000000000000047683384: 00110111001000010000000000000000 791 ms
0.000000000000000000000000000000000000000023841692: 01101110010000100000000000000000 802 ms
0.000000000000000000000000000000000000000011920846: 11011100100001000000000000000000 809 ms
0.000000000000000000000000000000000000000005961124: 01111001000010000000000000000000 795 ms
0.000000000000000000000000000000000000000002980562: 11110010000100000000000000000000 835 ms
0.000000000000000000000000000000000000000001490982: 00010100001000000000000000000000 864 ms
0.000000000000000000000000000000000000000000745491: 00101000010000000000000000000000 915 ms
0.000000000000000000000000000000000000000000372745: 01010000100000000000000000000000 918 ms
0.000000000000000000000000000000000000000000186373: 10100001000000000000000000000000 881 ms
0.000000000000000000000000000000000000000000092486: 01000010000000000000000000000000 857 ms
0.000000000000000000000000000000000000000000046243: 10000100000000000000000000000000 861 ms
0.000000000000000000000000000000000000000000022421: 00001000000000000000000000000000 855 ms
0.000000000000000000000000000000000000000000011210: 00010000000000000000000000000000 887 ms
0.000000000000000000000000000000000000000000005605: 00100000000000000000000000000000 799 ms
0.000000000000000000000000000000000000000000002803: 01000000000000000000000000000000 828 ms
0.000000000000000000000000000000000000000000001401: 10000000000000000000000000000000 815 ms
0.000000000000000000000000000000000000000000000000: 00000000000000000000000000000000 42 ms
0.000000000000000000000000000000000000000000000000: 00000000000000000000000000000000 42 ms
0.000000000000000000000000000000000000000000000000: 00000000000000000000000000000000 44 ms

এআরএম সম্পর্কে সমতুল্য আলোচনার স্ট্যাক ওভারফ্লো প্রশ্ন ডিএনআরমালাইজড ভাসটিং পয়েন্ট উদ্দেশ্য-সি-তে পাওয়া যাবে?


ডিমান্ডালাইজড ভাসমান বিন্দুতে স্বাগতম ! তারা পারফরম্যান্সের উপর বিধ্বংসী ধ্বংস করতে পারেন !!!

ডিএনআরমালাল (বা অস্বাভাবিক) সংখ্যাগুলি একটি হ্যাক ধরনের যা কিছু অতিরিক্ত মান ভাসমান বিন্দু প্রতিনিধিত্বের বাইরে শূন্যের কাছাকাছি থাকে। ডিমানমালাইজড ভাসমান-বিন্দুতে অপারেশনগুলি স্বাভাবিকভাবে ভাসমান-বিন্দুর তুলনায় শতগুণ ধীর হতে পারে। এটি কারণ অনেক প্রসেসর সরাসরি তাদের পরিচালনা করতে পারে না এবং মাইক্রোডোড ব্যবহার করে ফাঁদ এবং সমাধান করতে হবে।

যদি আপনি 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 সংখ্যা সাধারণত বিরল এবং এইভাবে অধিকাংশ প্রসেসর দক্ষতার সাথে তাদের পরিচালনা করার চেষ্টা করবেন না।

কোডটির শুরুতে এটি যোগ করে আমরা শূন্য শূন্যে ফ্লাশ করলে তা ডিমানমালাইজড সংখ্যার সাথে সবকিছু করতে হবে:

_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 উভয় loops বাইরে একটি রেজিস্টার মধ্যে রূপান্তর / সংরক্ষণ করা হয়। যাতে কর্মক্ষমতা কোন প্রভাব আছে।





floating-point