2011-07-27 25 views
17

फिर से वालराय के लिए मेरे प्रश्न के लिए क्षमा करें। मैं इसका उपयोग करने की कोशिश कर रहा हूं क्योंकि वेक्टर & मैट्रिक्स ऑपरेट करते समय मैटलैब की तरह है। मैंने पहले कुछ प्रदर्शन जांच की और पाया कि Valarray stroustrup द्वारा पुस्तक C++ प्रोग्रामिंग भाषा में घोषित प्रदर्शन को प्राप्त नहीं कर सकता है।वालराय इतना धीमा क्यों है?

परीक्षण कार्यक्रम वास्तव में युगल के 5 एम गुणा किया था। मैंने सोचा कि सी = ए * बी कम से कम लूप डबल टाइप तत्व गुणा के लिए तुलनीय होगा, लेकिन मैं पूरी तरह गलत हूं। कई कंप्यूटर और vc6.0 और बनाम 2008 पर कोशिश की।

वैसे, मैं निम्नलिखित कोड का उपयोग कर matlab पर परीक्षण किया:

len=5*1024*1024; 
a=rand(len,1);b=rand(len,1);c=zeros(len,1); 
tic;c=a.*b;toc; 

और परिणाम 46ms है। इस बार उच्च परिशुद्धता नहीं है, केवल संदर्भ के रूप में काम करता है।

कोड है:

#include <iostream> 
#include <valarray> 
#include <iostream> 
#include "windows.h" 

using namespace std ; 
SYSTEMTIME stime; 
LARGE_INTEGER sys_freq; 

double gettime_hp(); 

int main() 
{ 
    enum { N = 5*1024*1024 }; 
    valarray<double> a(N), b(N), c(N) ; 
    QueryPerformanceFrequency(&sys_freq); 
    int i,j; 
    for( j=0 ; j<8 ; ++j) 
    { 
     for( i=0 ; i<N ; ++i) 
     { 
      a[i]=rand(); 
      b[i]=rand(); 
     } 

     double* a1 = &a[0], *b1 = &b[0], *c1 = &c[0] ; 
     double dtime=gettime_hp(); 
     for( i=0 ; i<N ; ++i) c1[i] = a1[i] * b1[i] ; 
     dtime=gettime_hp()-dtime; 
     cout << "double operator* " << dtime << " ms\n" ; 

     dtime=gettime_hp(); 
     c = a*b ; 
     dtime=gettime_hp()-dtime; 
     cout << "valarray operator* " << dtime << " ms\n" ; 

     dtime=gettime_hp(); 
     for( i=0 ; i<N ; ++i) c[i] = a[i] * b[i] ; 
     dtime=gettime_hp()-dtime; 
     cout << "valarray[i] operator* " << dtime<< " ms\n" ; 

     cout << "------------------------------------------------------\n" ; 
    } 
} 

double gettime_hp() 
{ 
    LARGE_INTEGER tick; 
    extern LARGE_INTEGER sys_freq; 
    QueryPerformanceCounter(&tick); 
    return (double)tick.QuadPart*1000.0/sys_freq.QuadPart; 
} 

चल परिणाम: (अधिकतम गति अनुकूलन के साथ रिलीज़ मोड) एक ही अनुकूलन के साथ

double operator* 52.3019 ms 
valarray operator* 128.338 ms 
valarray[i] operator* 43.1801 ms 
------------------------------------------------------ 
double operator* 43.4036 ms 
valarray operator* 145.533 ms 
valarray[i] operator* 44.9121 ms 
------------------------------------------------------ 
double operator* 43.2619 ms 
valarray operator* 158.681 ms 
valarray[i] operator* 43.4871 ms 
------------------------------------------------------ 
double operator* 42.7317 ms 
valarray operator* 173.164 ms 
valarray[i] operator* 80.1004 ms 
------------------------------------------------------ 
double operator* 43.2236 ms 
valarray operator* 158.004 ms 
valarray[i] operator* 44.3813 ms 
------------------------------------------------------ 

डिबगिंग मोड:

double operator* 41.8123 ms 
valarray operator* 201.484 ms 
valarray[i] operator* 41.5452 ms 
------------------------------------------------------ 
double operator* 40.2238 ms 
valarray operator* 215.351 ms 
valarray[i] operator* 40.2076 ms 
------------------------------------------------------ 
double operator* 40.5859 ms 
valarray operator* 232.007 ms 
valarray[i] operator* 40.8803 ms 
------------------------------------------------------ 
double operator* 40.9734 ms 
valarray operator* 234.325 ms 
valarray[i] operator* 40.9711 ms 
------------------------------------------------------ 
double operator* 41.1977 ms 
valarray operator* 234.409 ms 
valarray[i] operator* 41.1429 ms 
------------------------------------------------------ 
double operator* 39.7754 ms 
valarray operator* 234.26 ms 
valarray[i] operator* 39.6338 ms 
------------------------------------------------------ 
+0

के रूप में लगभग समान है क्या आपने निष्पादन योग्य चलाया था? या आपने इसे डीबगर (विजुअल स्टूडियो के माध्यम से) में आजमाया है? –

+0

आप किस अनुकूलन सेटिंग्स का उपयोग कर रहे हैं? –

+0

डीबगर या exe – shangping

उत्तर

11

मुझे लगता है कि संदेह है कारण c = a*b ऑपरेशन को एक तत्व करने से बहुत धीमा है एक समय में

template<class T> valarray<T> operator* 
    (const valarray<T>&, const valarray<T>&); 

ऑपरेटर को परिणाम देने के लिए स्मृति आवंटित करना होगा, फिर उस मूल्य को वापस कर दें।

यहां तक ​​कि अगर एक "swaptimization" कॉपी प्रदर्शन करने के लिए प्रयोग किया जाता है, कि समारोह अभी भी जिसके परिणामस्वरूप valarray

  • आरंभ नई valarray (यह संभव है कि इस के लिए नए ब्लॉक का आवंटन

    • की भूमि के ऊपर है दूर अनुकूलित किया जा सकता है)
    • नई valarray के लिए स्मृति में नया valarray
    • पेजिंग में परिणाम डाल के रूप में यह प्रारंभ या परिणाम वैल के साथ सेट कर दिया जाता ues
    • वर्ष valarray कि परिणाम
  • +0

    मैं बस की खोज, यह वास्तव में एक संदर्भ रिटर्न: टेम्पलेट इनलाइन \t valarray <_Ty> और ऑपरेटर * = (valarray <_Ty> और _L, स्थिरांक _Ty और पुनः लोड) \t {_VALGOP2 (* = पुनः लोड); } – shangping

    +2

    उपरोक्त टिप्पणी में आपके द्वारा पोस्ट की गई घोषणा में दो मतभेद हैं जो प्रश्न में पोस्ट किए गए कोड में उपयोग किए जाएंगे: 1) 'ऑपरेटर * =' 'ऑपरेटर *() 'के बाद' ऑपरेटर =() ', और 2) यह' * = 'ऑपरेटर के लिए घोषणा है जो –

    +0

    माइकल द्वारा 'वैलर्रे' को गुणा करने के लिए स्केलर तर्क लेता है, माइकल आप सही हैं, मैं बहुत उत्साहित हूं – shangping

    22

    मैं सिर्फ एक लिनक्स x86-64 प्रणाली (सैंडी ब्रिज सीपीयू) पर इसे करने की कोशिश के द्वारा बदल दिया जाता है deallocating:

    जीसीसी 4.5.0:

    double operator* 9.64185 ms 
    valarray operator* 9.36987 ms 
    valarray[i] operator* 9.35815 ms 
    

    इंटेल आईसीसी 12.0.2:

    double operator* 7.76757 ms 
    valarray operator* 9.60208 ms 
    valarray[i] operator* 7.51409 ms 
    

    दोनों cas में es मैंने अभी -O3 और कोई अन्य अनुकूलन-संबंधित झंडे का उपयोग नहीं किया है।

    ऐसा लगता है कि एमएस सी ++ कंपाइलर और/या वालराय कार्यान्वयन चूसना है।

    #include <iostream> 
    #include <valarray> 
    #include <iostream> 
    #include <ctime> 
    
    using namespace std ; 
    
    double gettime_hp(); 
    
    int main() 
    { 
        enum { N = 5*1024*1024 }; 
        valarray<double> a(N), b(N), c(N) ; 
        int i,j; 
        for( j=0 ; j<8 ; ++j) 
        { 
         for( i=0 ; i<N ; ++i) 
         { 
          a[i]=rand(); 
          b[i]=rand(); 
         } 
    
         double* a1 = &a[0], *b1 = &b[0], *c1 = &c[0] ; 
         double dtime=gettime_hp(); 
         for( i=0 ; i<N ; ++i) c1[i] = a1[i] * b1[i] ; 
         dtime=gettime_hp()-dtime; 
         cout << "double operator* " << dtime << " ms\n" ; 
    
         dtime=gettime_hp(); 
         c = a*b ; 
         dtime=gettime_hp()-dtime; 
         cout << "valarray operator* " << dtime << " ms\n" ; 
    
         dtime=gettime_hp(); 
         for( i=0 ; i<N ; ++i) c[i] = a[i] * b[i] ; 
         dtime=gettime_hp()-dtime; 
         cout << "valarray[i] operator* " << dtime<< " ms\n" ; 
    
         cout << "------------------------------------------------------\n" ; 
        } 
    } 
    
    double gettime_hp() 
    { 
        struct timespec timestamp; 
    
        clock_gettime(CLOCK_REALTIME, &timestamp); 
        return timestamp.tv_sec * 1000.0 + timestamp.tv_nsec * 1.0e-6; 
    } 
    
    +1

    नाइस - बस संदर्भ के लिए, क्या आप निर्माण में उपयोग किए गए विकल्पों को जोड़ सकते हैं (मैं आज रात इस सामान के साथ खेल सकता हूं ...) –

    +2

    +1। मैं libC++ कार्यान्वयन पर इस बेंचमार्क भाग गया। यह एमएस जितना धीमा नहीं था, लेकिन जीसीसी जितना तेज़ नहीं था (यह उसी गति के बारे में था जब आप आईसीसी के लिए रिपोर्ट करते थे)। बाहर निकलता है मैं अभिव्यक्ति टेम्पलेट इंजन में एक महत्वपूर्ण असाइनमेंट ऑपरेटर खो रहा था। जोड़ा गया अब libC++ जीसीसी के रूप में तेज़ है। ओपी को: स्पीड टेस्ट के लिए धन्यवाद! (प्रश्न पर +1 भी) :-) –

    +0

    धन्यवाद दोनों - मैंने एक नोट पुनः कंपाइलर स्विच (दोनों मामलों में केवल '-ओ 3') जोड़ा है और लिनक्स के लिए संशोधित ओपी के कोड को भी जोड़ा है जिसे मैंने इन परीक्षणों के लिए उपयोग किया था। –

    1

    मैं रिहाई 64 में संकलन कर रहा हूँ, वी.एस. 2010 मैं बहुत थोड़ा अपने कोड बदल दिया है::


    यहाँ ओपी के लिनक्स के लिए संशोधित कोड है

    double* a1 = &a[0], *b1 = &b[0], *c1 = &c[0] ; 
        double dtime=gettime_hp(); 
        for( i=0 ; i<N ; ++i) a1[i] *= b1[i] ; 
        dtime=gettime_hp()-dtime; 
        cout << "double operator* " << dtime << " ms\n" ; 
    
        dtime=gettime_hp(); 
        a *= b; 
        dtime=gettime_hp()-dtime; 
        cout << "valarray operator* " << dtime << " ms\n" ; 
    
        dtime=gettime_hp(); 
        for( i=0 ; i<N ; ++i) a[i] *= b[i] ; 
        dtime=gettime_hp()-dtime; 
        cout << "valarray[i] operator* " << dtime<< " ms\n" ; 
    
        cout << "------------------------------------------------------\n" ; 
    

    यहाँ आप कर सकते हैं देखें कि मैंने 0 =के बजाय * = का उपयोग किया था। अधिक आधुनिक गणितीय पुस्तकालयों में, बहुत ही जटिल अभिव्यक्ति टेम्पलेट तंत्र का उपयोग किया जाता है जो इस समस्या को खत्म करते हैं। इस मामले में, मुझे वास्तव में Valarray से बहुत तेज़ परिणाम मिल गए, हालांकि शायद यह इसलिए है क्योंकि सामग्री पहले से ही कैश में थी। जो उपर आप देख रहे हैं वह केवल अनावश्यक अस्थायी है और वालराय के लिए आंतरिक कुछ भी नहीं है, विशेष रूप से- आप std::string जैसे कुछ व्यवहार के साथ समान व्यवहार देखेंगे।

    +1

    मैंने आपके परिणामों की पुष्टि की। हालांकि यह परिवर्तन मामूली बदलाव नहीं है। कई कंपाउंडिंग अभिव्यक्ति को हमेशा * =, + =/= – shangping

    +1

    @shangping का उपयोग करके नहीं किया जा सकता है: उस स्थिति में, यदि आपने आवश्यक अस्थायी चर के लिए एक नया परिणाम सरणी आवंटित की है, तो आपको 'डबल' के लिए एक समान मंदी दिखाई देगी। 'वालराय' के लिए। इस पोस्ट – Puppy

    +0

    +1 ... और परिणाम क्या था? –

    3

    मुझे अंततः देरी वाले मूल्यांकन का उपयोग करके यह मिला। कोड बदसूरत हो सकता है क्योंकि मैं इन सी ++ उन्नत अवधारणाओं को सीखना शुरू कर रहा हूं। अगर आपको बेहतर विचार है तो मुझे सही करें। आपकी सभी सहायता के लिए बहुत बहुत धन्यवाद। यहाँ कोड है:

    #include <iostream> 
    #include <valarray> 
    #include <iostream> 
    #include "windows.h" 
    
    using namespace std ; 
    SYSTEMTIME stime; 
    LARGE_INTEGER sys_freq; 
    
    double gettime_hp(); 
    //to improve the c=a*b (it will generate a temp first, assigned to c and delete the temp 
    //which causes the program really slow 
    //the solution is the expression template and let the compiler to decide when all the expression is known 
    //delayed evaluation 
    //typedef valarray<double> Vector; 
    class Vector; 
    class VecMul 
    { 
    public: 
        const Vector& va; 
        const Vector& vb; 
        //Vector& vc; 
        VecMul(const Vector& v1,const Vector& v2):va(v1),vb(v2){} 
        operator Vector(); 
    }; 
    
    class Vector:public valarray<double> 
    { 
        valarray<double> *p; 
    public: 
        explicit Vector(int n) 
        { 
         p=new valarray<double>(n); 
        } 
        Vector& operator=(const VecMul &m) 
        { 
         for(int i=0;i<m.va.size();i++) (*p)[i]=(m.va)[i]*(m.vb)[i];//ambiguous 
         return *this; 
        } 
        double& operator[](int i) const {return (*p)[i];} //const vector_type[i] 
        int size()const {return (*p).size();} 
    }; 
    
    
    
    inline VecMul operator*(const Vector& v1,const Vector& v2) 
    { 
        return VecMul(v1,v2); 
    } 
    
    
    int main() 
    { 
        enum { N = 5*1024*1024 }; 
        Vector a(N), b(N), c(N) ; 
        QueryPerformanceFrequency(&sys_freq); 
        int i,j; 
        for( j=0 ; j<8 ; ++j) 
        { 
         for( i=0 ; i<N ; ++i) 
         { 
          a[i]=rand(); 
          b[i]=rand(); 
         } 
    
         double* a1 = &a[0], *b1 = &b[0], *c1 = &c[0] ; 
         double dtime=gettime_hp(); 
         for( i=0 ; i<N ; ++i) c1[i] = a1[i] * b1[i] ; 
         dtime=gettime_hp()-dtime; 
         cout << "double operator* " << dtime << " ms\n" ; 
    
         dtime=gettime_hp(); 
         c = a*b ; 
         dtime=gettime_hp()-dtime; 
         cout << "valarray operator* " << dtime << " ms\n" ; 
    
         dtime=gettime_hp(); 
         for( i=0 ; i<N ; ++i) c[i] = a[i] * b[i] ; 
         dtime=gettime_hp()-dtime; 
         cout << "valarray[i] operator* " << dtime<< " ms\n" ; 
    
         cout << "------------------------------------------------------\n" ; 
        } 
    } 
    
    double gettime_hp() 
    { 
        LARGE_INTEGER tick; 
        extern LARGE_INTEGER sys_freq; 
        QueryPerformanceCounter(&tick); 
        return (double)tick.QuadPart*1000.0/sys_freq.QuadPart; 
    } 
    

    दृश्य स्टूडियो पर चल रहा है परिणाम है:

    double operator* 41.2031 ms 
    valarray operator* 43.8407 ms 
    valarray[i] operator* 42.49 ms 
    
    3

    valarray के पूरे मुद्दे वेक्टर मशीन है, जो 86 मशीनों बस नहीं हैं पर तेजी से हो रहा है। एक nonvector मशीन पर एक अच्छा कार्यान्वयन प्रदर्शन है कि आप की तरह
    for (i=0; i < N; ++i) c1[i] = a1[i] * b1[i];

    और निश्चित रूप से नहीं होगा एक बुरा एक कुछ के साथ मिलता है मिलान करने के लिए सक्षम होना चाहिए। जब तक समानांतर प्रसंस्करण में तेजी लाने के लिए हार्डवेयर में कुछ भी नहीं है, तो यह सबसे अच्छा होने वाला है जो आप कर सकते हैं।

    0

    hmm..I ब्लिट्ज परीक्षण किया है और इसकी एक ही रूप में valarray..and अधिक ब्लिट्ज ++ [] operatpr बहुत धीमी गति से

    #include <blitz/array.h> 
        #include <iostream> 
        #ifdef WIN32 
        #include "windows.h" 
        LARGE_INTEGER sys_freq; 
        #endif 
        #ifdef LINUX 
        <ctime> 
        #endif 
         using namespace std ; 
        SYSTEMTIME stime; 
    
    
        __forceinline double gettime_hp(); 
        double gettime_hp() 
        { 
        #ifdef WIN32 
         LARGE_INTEGER tick; 
         extern LARGE_INTEGER sys_freq; 
         QueryPerformanceCounter(&tick); 
         return (double)tick.QuadPart*1000.0/sys_freq.QuadPart; 
        #endif 
        #ifdef LINUX 
         struct timespec timestamp; 
    
         clock_gettime(CLOCK_REALTIME, &timestamp); 
         return timestamp.tv_sec * 1000.0 + timestamp.tv_nsec * 1.0e-6; 
        #endif 
        } 
        BZ_USING_NAMESPACE(blitz) 
    
        int main() 
        { 
         int N = 5*1024*1024 ; 
    
         // Create three-dimensional arrays of double 
         Array<double,1> a(N), b(N),c(N); 
    
    
         int i,j; 
        #ifdef WIN32 
         QueryPerformanceFrequency(&sys_freq); 
        #endif 
         for( j=0 ; j<8 ; ++j) 
         { 
          for( i=0 ; i<N ; ++i) 
          { 
           a[i]=rand(); 
           b[i]=rand(); 
          } 
    
          double* a1 = a.data() , *b1 = b.data(), *c1 = c.data() ; 
          double dtime=gettime_hp(); 
          for( i=0 ; i<N ; ++i) c1[i] = a1[i] * b1[i] ; 
          dtime=gettime_hp()-dtime; 
          cout << "double operator* " << dtime << " ms\n" ; 
    
          dtime=gettime_hp(); 
          c = a*b ; 
          dtime=gettime_hp()-dtime; 
          cout << "blitz operator* " << dtime << " ms\n" ; 
    
          dtime=gettime_hp(); 
          for( i=0 ; i<N ; ++i) c[i] = a[i] * b[i] ; 
          dtime=gettime_hp()-dtime; 
          cout << "blitz[i] operator* " << dtime<< " ms\n" ; 
    
          cout << "------------------------------------------------------\n" ; 
         } 
        } 
    
    +4

    के लिए एक वर्ष के उत्सव के रूप में – arman

    -1

    मुझे लगता है कि माइकल बर्र के जवाब सही है। और हो सकता है कि आप वर्चुअल प्रकार को ऑपरेटर + के रिटर्न वैल्यू के प्रकार के रूप में बना सकें, और इस आभासी प्रकार जैसे ऑपरेटर = (आभासी प्रकार & v) के लिए एक और ऑपरेटर = पुनः लोड करें {& valarray = & v; v = NULL;} (मोटे तौर पर बोला जा रहा है)। बेशक, Valarray पर विचार को लागू करना मुश्किल है। लेकिन जब आप एक नई कक्षा बनाते हैं, तो आप इस विचार को आजमा सकते हैं। और फिर, ऑपरेटर + के लिए दक्षता ऑपरेटर + =

    +0

    यह उनके जवाब पर एक टिप्पणी होनी चाहिए, न कि एक नया जवाब। – Allan

    संबंधित मुद्दे