आधुनिक हार्डवेयर पर फ्लोटिंग पॉइंट बनाम पूर्णांक गणना


100

मैं C ++ में कुछ प्रदर्शन महत्वपूर्ण काम कर रहा हूं, और हम वर्तमान में उन समस्याओं के लिए पूर्णांक गणना का उपयोग कर रहे हैं जो स्वाभाविक रूप से अस्थायी बिंदु हैं क्योंकि "तेज"। यह पूरी तरह से कष्टप्रद समस्याओं का कारण बनता है और बहुत सारे कष्टप्रद कोड जोड़ता है।

अब, मुझे याद है कि फ्लोटिंग पॉइंट की गणना 386 दिनों में लगभग कितनी धीमी थी, जहां मुझे विश्वास है कि (IIRC) एक वैकल्पिक सह-प्रॉसेसर था। लेकिन निश्चित रूप से आजकल तेजी से अधिक जटिल और शक्तिशाली सीपीयू के साथ यह फ्लोटिंग पॉइंट या पूर्णांक गणना करते समय "गति" में कोई अंतर नहीं करता है? विशेष रूप से चूंकि वास्तविक गणना समय कुछ की तुलना में छोटा होता है जैसे कि पाइपलाइन स्टाल या मुख्य मेमोरी से कुछ प्राप्त करना?

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

मैंने जिन कार्यक्रमों का उपयोग इस प्रकार किया है, वे किसी भी तरह से समान नहीं हैं:

#include <iostream>
#include <cmath>
#include <cstdlib>
#include <time.h>

int main( int argc, char** argv )
{
    int accum = 0;

    srand( time( NULL ) );

    for( unsigned int i = 0; i < 100000000; ++i )
    {
        accum += rand( ) % 365;
    }
    std::cout << accum << std::endl;

    return 0;
}

कार्यक्रम 2:

#include <iostream>
#include <cmath>
#include <cstdlib>
#include <time.h>

int main( int argc, char** argv )
{

    float accum = 0;
    srand( time( NULL ) );

    for( unsigned int i = 0; i < 100000000; ++i )
    {
        accum += (float)( rand( ) % 365 );
    }
    std::cout << accum << std::endl;

    return 0;
}

अग्रिम में धन्यवाद!

संपादित करें: जिस प्लेटफॉर्म की मुझे परवाह है, वह नियमित रूप से x86 या x86-64 डेस्कटॉप लिनक्स और विंडोज मशीनों पर चल रहा है।

2 संपादित करें (नीचे एक टिप्पणी से चिपकाया गया): हमारे पास वर्तमान में एक व्यापक कोड आधार है। वास्तव में मैं सामान्यीकरण के खिलाफ आया हूं कि हमें "फ्लोट का उपयोग नहीं करना चाहिए क्योंकि पूर्णांक गणना तेज है" - और मैं इस सामान्यीकृत धारणा को बाधित करने के लिए एक रास्ता खोज रहा हूं (यदि यह भी सच है)। मुझे एहसास है कि सभी कार्यों को करने और बाद में इसकी रूपरेखा तैयार करने में हमारे लिए सटीक परिणाम की भविष्यवाणी करना असंभव होगा।

वैसे भी, आपके सभी उत्कृष्ट उत्तर और मदद के लिए धन्यवाद। कुछ और जोड़ने के लिए स्वतंत्र महसूस करें :)।


8
अब आपके पास परीक्षण के रूप में जो कुछ भी है वह तुच्छ है। असेंबली में संभवतः बहुत कम अंतर है, ( उदाहरण के लिए, addlप्रतिस्थापित fadd)। वास्तव में एक अच्छा माप प्राप्त करने का एकमात्र तरीका आपके वास्तविक कार्यक्रम और उस के विभिन्न संस्करणों की रूपरेखा का एक मुख्य भाग है। दुर्भाग्य से यह बहुत कठिन प्रयास के टन का उपयोग किए बिना हो सकता है। शायद हमें टारगेट हार्डवेयर और आपका कंपाइलर बताने से लोगों को कम से कम आपको पहले से मौजूद अनुभव देने में मदद मिलेगी, आदि। आपके पूर्णांक उपयोग के बारे में, मुझे संदेह है कि आप एक तरह का fixed_pointटेम्प्लेट क्लास बना सकते हैं जो इस तरह के काम को जबरदस्त रूप से आसान कर देगा।
GMANNICKG

1
अभी भी बहुत सारे आर्किटेक्चर हैं जो अस्थायी फ़्लोटिंग हार्डवेयर को समर्पित नहीं करते हैं - कुछ टैग जो आपके द्वारा देखभाल की जाने वाली प्रणालियों की व्याख्या करते हैं, आपको बेहतर उत्तर देने में मदद करेंगे।
कार्ल नॉरम

3
मेरा मानना ​​है कि मेरे HTC Hero (android) में हार्डवेयर FPU नहीं है, लेकिन Google NexusOne (Android) में हार्डवेयर है। आपका निशाना क्या है? डेस्कटॉप / सर्वर पीसी? netbooks (संभव हाथ + लिनक्स)? फोन?
स्टीलबाइट्स

5
यदि आप x86 पर तेज एफपी चाहते हैं, तो अनुकूलन और एसएसई कोड पीढ़ी के साथ संकलन करने का प्रयास करें। SSE (जो भी संस्करण) कम से कम फ्लोट ऐड, घटाना और एक ही चक्र में गुणा कर सकता है। डिवाइड, मॉड, और उच्च फ़ंक्शन हमेशा धीमा रहेगा । यह भी ध्यान दें कि floatगति को बढ़ावा मिलता है, लेकिन आमतौर पर doubleनहीं होता है।
माइक डी।

1
फिक्स्ड-पॉइंट पूर्णांक परिणाम को ओवरफ़्लो होने से बचाने के लिए कई पूर्णांक ऑपरेशनों का उपयोग करके FP का अनुमान लगाता है। आधुनिक डेस्कटॉप सीपीयू में पाए जाने वाले बेहद सक्षम एफपीयू का उपयोग करने की तुलना में यह लगभग हमेशा धीमा है। उदाहरण के लिए, MAD, फिक्स्ड-पॉइंट mp3 डिकोडर, libmpg123 की तुलना में धीमा है, और भले ही यह एक निश्चित बिंदु विकोडक के लिए अच्छी गुणवत्ता है, लेकिन libmpg123 में अभी भी कम गोलाई की त्रुटि है। पीपीसी जी 5 पर बेंचमार्क के लिए wezm.net/technical/2008/04/mp3-decoder-lbooks-compared
पीटर कॉर्डेस

जवाबों:


35

काश, मैं आपको केवल "यह निर्भर करता है" उत्तर दे सकता है ...

मेरे अनुभव से, प्रदर्शन करने के लिए कई, कई चर हैं ... विशेष रूप से पूर्णांक और फ्लोटिंग पॉइंट गणित के बीच। यह प्रोसेसर से प्रोसेसर (यहां तक ​​कि एक ही परिवार जैसे कि x86) में दृढ़ता से भिन्न होता है क्योंकि विभिन्न प्रोसेसर में अलग-अलग "पाइपलाइन" लंबाई होती है। इसके अलावा, कुछ ऑपरेशन आम तौर पर बहुत सरल होते हैं (जैसे कि अतिरिक्त) और प्रोसेसर के माध्यम से एक त्वरित मार्ग होता है, और अन्य (जैसे विभाजन) बहुत लंबे समय तक लेते हैं।

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

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

* नोट: पुराने एमएमएक्स निर्देशों का उपयोग करना वास्तव में कार्यक्रमों को धीमा कर देगा, क्योंकि उन पुराने निर्देशों ने वास्तव में एफपीयू के समान ही रजिस्टरों का उपयोग किया था, जिससे एक ही समय में एफपीयू और एमएमएक्स दोनों का उपयोग करना असंभव हो गया।


8
और कुछ प्रोसेसर पर एफपी गणित पूर्णांक गणित से तेज हो सकता है। अल्फा प्रोसेसर में एक FP विभाजन निर्देश था लेकिन पूर्णांक एक नहीं था, इसलिए पूर्णांक विभाजन को सॉफ्टवेयर में किया जाना था।
गाबे

क्या SSEx भी दोहरी परिशुद्धता अंकगणित में तेजी लाएगा? मुझे क्षमा करें, मैं SSE
जोहान्स स्काउब -

1
@ जोहान्सचैब-लिटब: SSE2 (x86-64 के लिए आधार रेखा) में पैक- doubleएफपीआरपी है। केवल दो 64-बिट doubleप्रति रजिस्टर के साथ, संभावित स्पीडअप floatउस कोड के लिए छोटा है जो अच्छी तरह से वेक्टर करता है। स्केलर floatऔर doublex86-64 पर एक्सएमएम रजिस्टरों का उपयोग करें, केवल विरासत के लिए x87 के साथ उपयोग किया जाता है long double। (इसलिए @ दान: नहीं, MMX रजिस्टर सामान्य FPU रजिस्टरों के साथ संघर्ष नहीं करते हैं, क्योंकि x86-64 पर सामान्य FPU SSE इकाई है। MMX व्यर्थ होगा क्योंकि यदि आप पूर्णांक SIMD कर सकते हैं, तो आप xmm0..158 के बजाय 16-बायर्स चाहते हैं। -बेटे mm0..7, और आधुनिक सीपीयू में SSE थ्रूपुट से भी बदतर MMX है।)
पीटर कॉर्ड्स

1
लेकिन MMX और SSE * / AVX2 पूर्णांक निर्देश समान निष्पादन इकाइयों के लिए प्रतिस्पर्धा करते हैं, इसलिए दोनों का एक बार उपयोग करना लगभग कभी उपयोगी नहीं होता है। अधिक काम पाने के लिए बस विस्तृत एक्सएमएम / वाईएमएम संस्करणों का उपयोग करें। एक ही समय में SIMD पूर्णांक और FP का उपयोग करना समान रजिस्टरों के लिए प्रतिस्पर्धा करता है, लेकिन x86-64 में उनमें से 16 हैं। लेकिन कुल थ्रूपुट सीमा का मतलब है कि आप समानांतर में पूर्णांक और एफपी निष्पादन इकाइयों का उपयोग करके दोगुना काम नहीं कर सकते।
पीटर कॉर्डेस

49

उदाहरण के लिए (कम संख्या में तेज हैं),

64-बिट इंटेल Xeon X5550 @ 2.67GHz, 4.1.2 gcc -O3

short add/sub: 1.005460 [0]
short mul/div: 3.926543 [0]
long add/sub: 0.000000 [0]
long mul/div: 7.378581 [0]
long long add/sub: 0.000000 [0]
long long mul/div: 7.378593 [0]
float add/sub: 0.993583 [0]
float mul/div: 1.821565 [0]
double add/sub: 0.993884 [0]
double mul/div: 1.988664 [0]

32-बिट ड्यूल कोर AMD Opteron (tm) प्रोसेसर 265 @ 1.81GHz, gcc 3.4.6 -O3

short add/sub: 0.553863 [0]
short mul/div: 12.509163 [0]
long add/sub: 0.556912 [0]
long mul/div: 12.748019 [0]
long long add/sub: 5.298999 [0]
long long mul/div: 20.461186 [0]
float add/sub: 2.688253 [0]
float mul/div: 4.683886 [0]
double add/sub: 2.700834 [0]
double mul/div: 4.646755 [0]

जैसा कि डैन ने बताया , यहां तक ​​कि एक बार जब आप घड़ी की आवृत्ति के लिए सामान्य हो जाते हैं (जो कि पाइपलाइज्ड डिजाइनों में खुद को गुमराह कर सकते हैं), तो परिणाम सीपीयू वास्तुकला (व्यक्तिगत ALU / FPU प्रदर्शन , साथ ही ALUs / FPUs की वास्तविक संख्या प्रति के आधार पर अलग-अलग रूप से उपलब्ध होंगे) सुपरस्क्लेरर डिज़ाइन में कोर जो प्रभावित करता है कि समानांतर में कितने स्वतंत्र संचालन निष्पादित कर सकते हैं - बाद के कारक को नीचे दिए गए कोड द्वारा प्रयोग नहीं किया जाता है क्योंकि नीचे सभी संचालन क्रमिक रूप से निर्भर हैं।)

गरीब आदमी का FPU / ALU ऑपरेशन बेंचमार्क:

#include <stdio.h>
#ifdef _WIN32
#include <sys/timeb.h>
#else
#include <sys/time.h>
#endif
#include <time.h>
#include <cstdlib>

double
mygettime(void) {
# ifdef _WIN32
  struct _timeb tb;
  _ftime(&tb);
  return (double)tb.time + (0.001 * (double)tb.millitm);
# else
  struct timeval tv;
  if(gettimeofday(&tv, 0) < 0) {
    perror("oops");
  }
  return (double)tv.tv_sec + (0.000001 * (double)tv.tv_usec);
# endif
}

template< typename Type >
void my_test(const char* name) {
  Type v  = 0;
  // Do not use constants or repeating values
  //  to avoid loop unroll optimizations.
  // All values >0 to avoid division by 0
  // Perform ten ops/iteration to reduce
  //  impact of ++i below on measurements
  Type v0 = (Type)(rand() % 256)/16 + 1;
  Type v1 = (Type)(rand() % 256)/16 + 1;
  Type v2 = (Type)(rand() % 256)/16 + 1;
  Type v3 = (Type)(rand() % 256)/16 + 1;
  Type v4 = (Type)(rand() % 256)/16 + 1;
  Type v5 = (Type)(rand() % 256)/16 + 1;
  Type v6 = (Type)(rand() % 256)/16 + 1;
  Type v7 = (Type)(rand() % 256)/16 + 1;
  Type v8 = (Type)(rand() % 256)/16 + 1;
  Type v9 = (Type)(rand() % 256)/16 + 1;

  double t1 = mygettime();
  for (size_t i = 0; i < 100000000; ++i) {
    v += v0;
    v -= v1;
    v += v2;
    v -= v3;
    v += v4;
    v -= v5;
    v += v6;
    v -= v7;
    v += v8;
    v -= v9;
  }
  // Pretend we make use of v so compiler doesn't optimize out
  //  the loop completely
  printf("%s add/sub: %f [%d]\n", name, mygettime() - t1, (int)v&1);
  t1 = mygettime();
  for (size_t i = 0; i < 100000000; ++i) {
    v /= v0;
    v *= v1;
    v /= v2;
    v *= v3;
    v /= v4;
    v *= v5;
    v /= v6;
    v *= v7;
    v /= v8;
    v *= v9;
  }
  // Pretend we make use of v so compiler doesn't optimize out
  //  the loop completely
  printf("%s mul/div: %f [%d]\n", name, mygettime() - t1, (int)v&1);
}

int main() {
  my_test< short >("short");
  my_test< long >("long");
  my_test< long long >("long long");
  my_test< float >("float");
  my_test< double >("double");

  return 0;
}

8
आपने बहु और div को क्यों मिलाया? क्या यह दिलचस्प नहीं होना चाहिए अगर बहु ​​हो (या उम्मीद से?) बहुत तेजी से फिर तलाक?
कीस ताओ

13
गुणक पूर्णांक और फ्लोटिंग पॉइंट दोनों मामलों में विभाजन की तुलना में बहुत तेज है। डिवीजन का प्रदर्शन संख्याओं के आकार पर भी निर्भर करता है। मैं आमतौर पर मानता हूं कि विभाजन ~ 15 गुना धीमा है।
सोगार्टार

4
pastebin.com/Kx8WGUfg मैंने आपका बेंचमार्क लिया और प्रत्येक ऑपरेशन को अपने स्वयं के लूप से अलग किया और volatileयह सुनिश्चित करने के लिए जोड़ा । Win64 पर, FPU अप्रयुक्त है और MSVC इसके लिए कोड उत्पन्न नहीं करेगा, इसलिए यह वहाँ mulssऔर divssXMM निर्देशों का उपयोग कर संकलन करता है , जो Win32 में FPU की तुलना में 25x तेज़ हैं। टेस्ट मशीन Core i5 M 520 @ 2.40GHz
जेम्स ड्यूने

4
@JamesDunne बस सावधान रहें, fp ऑप्स के vलिए जल्दी या तो 0 या + / -inf तक बहुत जल्दी पहुंच जाएगा, जो कि (विशेष रूप से) एक विशेष मामले के रूप में इलाज किया जा सकता है या नहीं हो सकता है / कुछ fpu कार्यान्वयन द्वारा fastpatheed।
vladr

3
इस "बेंचमार्क" में आउट-ऑफ-ऑर्डर निष्पादन के लिए कोई डेटा समानता नहीं है, क्योंकि प्रत्येक ऑपरेशन एक ही संचायक ( v) के साथ किया जाता है । हाल के इंटेल डिज़ाइनों पर, विभाजन को बिल्कुल ( divss/ divps10-14 चक्र विलंबता, और एक ही पारस्परिक प्रवाह) पाइपलाइज़ नहीं किया गया है। mulssहालाँकि, 5 चक्र विलंबता है, लेकिन हर चक्र को जारी कर सकता है। (या हेस्वेल पर दो प्रति चक्र, चूंकि पोर्ट 0 और पोर्ट 1 दोनों में एफएमए के लिए गुणक है)।
पीटर कॉर्डेस

23

फिक्स्ड-पॉइंट और फ्लोटिंग-पॉइंट गणित के बीच वास्तविक दुनिया की गति में एक महत्वपूर्ण अंतर होने की संभावना है, लेकिन ALU बनाम FPU के सैद्धांतिक सर्वश्रेष्ठ-केस थ्रूपुट पूरी तरह से अप्रासंगिक है। इसके बजाय, आपके आर्किटेक्चर पर पूर्णांक और फ्लोटिंग-पॉइंट रजिस्टर (असली रजिस्टर, न कि नाम रजिस्टर) की संख्या जो अन्यथा आपकी गणना (जैसे लूप नियंत्रण के लिए) द्वारा उपयोग नहीं की जाती है, प्रत्येक प्रकार के तत्वों की संख्या जो एक कैश लाइन में फिट होती हैं , पूर्णांक बनाम फ्लोटिंग पॉइंट गणित के लिए अलग-अलग शब्दार्थों पर विचार करना संभव है - ये प्रभाव हावी होंगे। आपके एल्गोरिथ्म की डेटा निर्भरता यहां एक महत्वपूर्ण भूमिका निभाती है, ताकि कोई भी सामान्य तुलना आपकी समस्या पर प्रदर्शन अंतर का अनुमान न लगा सके।

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

आपको एक समय में अधिक पूर्णांक संचालकों को कैश में फिट करने की संभावना है। इसलिए फिक्स्ड-पॉइंट संस्करण एक मशीन पर भी परिमाण के क्रम से फ्लोट संस्करण को बेहतर बना सकता है, जहां एफपीयू में सैद्धांतिक रूप से उच्चतर थ्रूपुट है।


4
+1 यह इंगित करने के लिए कि अनियंत्रित निरंतर पूर्णांक कार्यों के कारण भोली बेंचमार्क 0-टाइम लूप कैसे प्राप्त कर सकते हैं। इसके अलावा, कंपाइलर लूप (पूर्णांक या एफपी) को पूरी तरह से छोड़ सकता है यदि परिणाम वास्तव में उपयोग नहीं किया जाता है।
vladr

इसका निष्कर्ष यह है: किसी फ़ंक्शन को लूपिंग चर के रूप में तर्क के रूप में कॉल करना चाहिए। चूंकि मुझे लगता है कि कोई कंपाइलर यह देखने में सक्षम नहीं हो सकता है कि फ़ंक्शन कुछ भी नहीं करता है और कॉल को अनदेखा किया जा सकता है। चूंकि कॉल ओवरहेड है, केवल समय == का अंतर (फ़्लोट टाइम - पूर्णांक समय) महत्वपूर्ण होगा।
GameAlchemist

@GameAlchemist: कई कंपाइलर खाली कार्यों के लिए कॉल को समाप्त करते हैं, इनलाइनिंग के साइड इफेक्ट के रूप में। आपको इसे रोकने के लिए एक प्रयास करना होगा।
बेन वोइग्ट

ओपी को लग रहा था कि वह उन चीजों के लिए पूर्णांक का उपयोग करने के बारे में बात कर रहा है जहां एफपी अधिक प्राकृतिक रूप से फिट होगा, इसलिए यह एफपी कोड के समान परिणाम प्राप्त करने के लिए अधिक पूर्णांक कोड लेगा। इस मामले में, बस एफपी का उपयोग करें। उदाहरण के लिए, एक FPU (उदाहरण के लिए एक डेस्कटॉप सीपीयू) के साथ हार्डवेयर पर, फ़्लोट-पॉइंट डिकोडर्स की तुलना में फिक्स्ड-पॉइंट पूर्णांक एमपी 3 डिकोडर धीमे (और थोड़े अधिक गोल त्रुटियों) हैं। कोडेक्स के फिक्स्ड-पॉइंट कार्यान्वयन मुख्य रूप से बिना एफपी हार्डवेयर के छीन-डाउन एआरएम सीपीयू पर चलने के लिए मौजूद हैं, केवल एफपी को धीमा करते हैं।
पीटर कॉर्डेस

पहले बिंदु के लिए एक उदाहरण: AV86-512 के साथ x86-64 पर केवल 16 GP रजिस्टर हैं, लेकिन 32 zmm के रजिस्टरों में अदिश फ़्लोटिंग-पॉइंट गणित तेज़ हो सकता है
phuclv

18

इसके अलावा जोड़ बहुत तेज है rand, इसलिए आपका कार्यक्रम (विशेष रूप से) बेकार है।

आपको प्रदर्शन हॉटस्पॉट की पहचान करने और अपने कार्यक्रम को बढ़ाने की आवश्यकता है। ऐसा लगता है कि आपको अपने विकास के वातावरण के साथ समस्याएं हैं जिन्हें पहले हल करने की आवश्यकता होगी। क्या छोटी सी समस्या के लिए अपने पीसी पर अपना प्रोग्राम चलाना असंभव है?

आम तौर पर, पूर्णांक अंकगणित के साथ एफपी नौकरियों का प्रयास धीमा के लिए एक नुस्खा है।


हाँ, और साथ ही एक रैंड पूर्णांक से फ़्लोटिंग फ़्लोटिंग संस्करण में फ़्लोट में रूपांतरण। इसे परखने के बेहतर तरीके पर कोई विचार?
अधिकतम पेंगुइन

1
यदि आप प्रोफ़ाइल गति की कोशिश कर रहे हैं, तो POSIX timespec_tया कुछ इसी तरह देखें। लूप के शुरू और अंत में समय रिकॉर्ड करें और अंतर लें। फिर randडेटा पीढ़ी को लूप से बाहर ले जाएं । सुनिश्चित करें कि आपका एल्गोरिथ्म अपने सभी डेटा को सरणियों से प्राप्त करता है और अपने सभी डेटा को सरणियों में डालता है। यह आपके वास्तविक एल्गोरिथ्म को अपने आप ही प्राप्त करता है, और सेटअप, मॉलोक, रिजल्ट प्रिंटिंग, सब कुछ लेकिन कार्य स्विचन और आपके प्रोफाइलिंग लूप को बाधित करता है।
माइक डी।

3
@maxpenguin: सवाल यह है कि आप क्या परीक्षण कर रहे हैं। आर्टेम ने माना है कि आप ग्राफिक्स कर रहे हैं, कार्ल ने विचार किया कि क्या आप एक एम्बेडेड प्लेटफ़ॉर्म संस एफपी पर हैं, मेरा मानना ​​है कि आप एक सर्वर के लिए विज्ञान कोडिंग कर रहे हैं। आप बेंचमार्क को सामान्य या "लिख" नहीं सकते। बेंचमार्क आपके प्रोग्राम द्वारा किए जाने वाले वास्तविक कार्य से लिए जाते हैं। एक बात जो मैं आपको बता सकता हूं कि यदि आप अपने कार्यक्रम में प्रदर्शन-महत्वपूर्ण तत्व को छूते हैं, तो यह "अनिवार्य रूप से समान गति" नहीं रहेगा।
पोटाटोज़वाटर

अच्छी बात और अच्छा जवाब। वर्तमान में हमारे पास व्यापक कोड बेस है। वास्तव में मैं सामान्यीकरण के खिलाफ आया हूं कि हमें "फ्लोट का उपयोग नहीं करना चाहिए क्योंकि पूर्णांक गणना तेज है" - और मैं इस सामान्यीकृत धारणा को बाधित करने के लिए एक रास्ता खोज रहा हूं (यदि यह भी सच है)। मुझे एहसास है कि सभी कार्यों को करने और बाद में इसकी रूपरेखा तैयार करने में हमारे लिए सटीक परिणाम की भविष्यवाणी करना असंभव होगा। चलो, आपकी मदद के लिए धन्यवाद।
अधिकतम पेंगुइन

18

TIL यह भिन्न होता है (बहुत)। यहाँ गन्न संकलक (btw मैं भी मशीनों पर संकलित करके जाँच की जाती है, का उपयोग करते हुए कुछ परिणाम हैं, xenial से gnu g ++ 5.4 एक बहुत तेजी से नरक का एक नरक है। सटीक पर लिनो से 4.6.3)

इंटेल i7 4700MQ xenial

short add: 0.822491
short sub: 0.832757
short mul: 1.007533
short div: 3.459642
long add: 0.824088
long sub: 0.867495
long mul: 1.017164
long div: 5.662498
long long add: 0.873705
long long sub: 0.873177
long long mul: 1.019648
long long div: 5.657374
float add: 1.137084
float sub: 1.140690
float mul: 1.410767
float div: 2.093982
double add: 1.139156
double sub: 1.146221
double mul: 1.405541
double div: 2.093173

इंटेल i3 2370M के समान परिणाम हैं

short add: 1.369983
short sub: 1.235122
short mul: 1.345993
short div: 4.198790
long add: 1.224552
long sub: 1.223314
long mul: 1.346309
long div: 7.275912
long long add: 1.235526
long long sub: 1.223865
long long mul: 1.346409
long long div: 7.271491
float add: 1.507352
float sub: 1.506573
float mul: 2.006751
float div: 2.762262
double add: 1.507561
double sub: 1.506817
double mul: 1.843164
double div: 2.877484

इंटेल (R) सेलेरॉन (R) 2955U (एसर C720 क्रोमबुक xenial चल रहा है)

short add: 1.999639
short sub: 1.919501
short mul: 2.292759
short div: 7.801453
long add: 1.987842
long sub: 1.933746
long mul: 2.292715
long div: 12.797286
long long add: 1.920429
long long sub: 1.987339
long long mul: 2.292952
long long div: 12.795385
float add: 2.580141
float sub: 2.579344
float mul: 3.152459
float div: 4.716983
double add: 2.579279
double sub: 2.579290
double mul: 3.152649
double div: 4.691226

DigitalOcean 1GB ड्रॉपलेट इंटेल (R) Xeon (R) CPU E5-2630L v2 (भरोसेमंद ट्रस्ट)

short add: 1.094323
short sub: 1.095886
short mul: 1.356369
short div: 4.256722
long add: 1.111328
long sub: 1.079420
long mul: 1.356105
long div: 7.422517
long long add: 1.057854
long long sub: 1.099414
long long mul: 1.368913
long long div: 7.424180
float add: 1.516550
float sub: 1.544005
float mul: 1.879592
float div: 2.798318
double add: 1.534624
double sub: 1.533405
double mul: 1.866442
double div: 2.777649

AMD Opteron (tm) प्रोसेसर 4122 (सटीक)

short add: 3.396932
short sub: 3.530665
short mul: 3.524118
short div: 15.226630
long add: 3.522978
long sub: 3.439746
long mul: 5.051004
long div: 15.125845
long long add: 4.008773
long long sub: 4.138124
long long mul: 5.090263
long long div: 14.769520
float add: 6.357209
float sub: 6.393084
float mul: 6.303037
float div: 17.541792
double add: 6.415921
double sub: 6.342832
double mul: 6.321899
double div: 15.362536

इस से कोड का उपयोग करता http://pastebin.com/Kx8WGUfg के रूप मेंbenchmark-pc.c

g++ -fpermissive -O3 -o benchmark-pc benchmark-pc.c

मैंने कई पास चलाए हैं, लेकिन ऐसा लगता है कि सामान्य संख्या समान हैं।

एक उल्लेखनीय अपवाद ALU mul बनाम FPU mul प्रतीत होता है। जोड़ और घटाव तुच्छ रूप से भिन्न प्रतीत होते हैं।

यहां चार्ट फॉर्म में ऊपर दिया गया है (पूर्ण आकार के लिए क्लिक करें, कम तेजी से और बेहतर है):

उपरोक्त आंकड़ों का चार्ट

@Peter कॉर्ड्स को समायोजित करने के लिए अद्यतन करें

https://gist.github.com/Lewiscowles1986/90191c59c9aedf3d08bf0b129065cccc

i7 4700MQ लिनक्स उबंटू क्निअल 64-बिट (2018-03-13 को सभी पैच लागू)
    short add: 0.773049
    short sub: 0.789793
    short mul: 0.960152
    short div: 3.273668
      int add: 0.837695
      int sub: 0.804066
      int mul: 0.960840
      int div: 3.281113
     long add: 0.829946
     long sub: 0.829168
     long mul: 0.960717
     long div: 5.363420
long long add: 0.828654
long long sub: 0.805897
long long mul: 0.964164
long long div: 5.359342
    float add: 1.081649
    float sub: 1.080351
    float mul: 1.323401
    float div: 1.984582
   double add: 1.081079
   double sub: 1.082572
   double mul: 1.323857
   double div: 1.968488
AMD Opteron (tm) प्रोसेसर 4122 (सटीक, ड्रीमहोस्ट साझा-होस्टिंग)
    short add: 1.235603
    short sub: 1.235017
    short mul: 1.280661
    short div: 5.535520
      int add: 1.233110
      int sub: 1.232561
      int mul: 1.280593
      int div: 5.350998
     long add: 1.281022
     long sub: 1.251045
     long mul: 1.834241
     long div: 5.350325
long long add: 1.279738
long long sub: 1.249189
long long mul: 1.841852
long long div: 5.351960
    float add: 2.307852
    float sub: 2.305122
    float mul: 2.298346
    float div: 4.833562
   double add: 2.305454
   double sub: 2.307195
   double mul: 2.302797
   double div: 5.485736
Intel Xeon E5-2630L v2 @ 2.4GHz (भरोसेमंद 64-बिट, DigitalOcean VPS)
    short add: 1.040745
    short sub: 0.998255
    short mul: 1.240751
    short div: 3.900671
      int add: 1.054430
      int sub: 1.000328
      int mul: 1.250496
      int div: 3.904415
     long add: 0.995786
     long sub: 1.021743
     long mul: 1.335557
     long div: 7.693886
long long add: 1.139643
long long sub: 1.103039
long long mul: 1.409939
long long div: 7.652080
    float add: 1.572640
    float sub: 1.532714
    float mul: 1.864489
    float div: 2.825330
   double add: 1.535827
   double sub: 1.535055
   double mul: 1.881584
   double div: 2.777245

gcc5 शायद ऑटो-वेक्टराइज़ करता है जो gcc4.6 नहीं है? है benchmark-pcप्रवाह और विलंबता के कुछ संयोजन को मापने? आपके हैसवेल (i7 4700MQ) पर पूर्णांक 1 प्रति घड़ी थ्रूपुट, 3 चक्र विलंबता है, लेकिन पूर्णांक जोड़ / उप 4 प्रति घड़ी थ्रूपुट, 1 चक्र विलंबता ( agner.org/optimize ) है। तो संभवतः लूप ओवरहेड की एक बहुत कुछ है जो जोड़ने और खच्चर के लिए उन नंबरों को पतला करने के लिए इतना करीब आ गया है (लंबे समय तक: 0.824088 बनाम लंबी खच्चर: 1.017164)। (जीसीसी चूक को अनियंत्रित नहीं करने के लिए, पूरी तरह से बहुत कम पुनरावृत्ति मायने रखता है को छोड़कर)।
पीटर कॉर्डेस

और BTW, यह परीक्षण क्यों नहीं करता है int, केवल shortऔर long? लिनक्स x86-64 पर, short16 बिट, जबकि है (और इस तरह कुछ मामलों में आंशिक-रजिस्टर मंदी है) longऔर long longदोनों 64-बिट प्रकार हैं। (शायद यह विंडोज़ के लिए डिज़ाइन किया गया है जहाँ x86-64 अभी भी 32-बिट का उपयोग करता है; longया हो सकता है कि इसे 32-बिट मोड के लिए डिज़ाइन किया गया हो।) लिनक्स पर, x32 ABI longमें 64-बिट मोड में 32-बिट है , इसलिए यदि आपने लाइब्रेरी स्थापित की है , gcc -mx32ILP32 के लिए संकलक का उपयोग करें। या बस संख्याओं का उपयोग करें -m32और देखें long
पीटर कॉर्ड्स

और अगर आपके कंपाइलर ने किसी भी चीज़ को आटो-वेक्टर किया है तो आपको वास्तव में जांचना चाहिए। उदाहरण addpsके लिए addss, 4 एफपी करने के बजाय एक्सएमएम रजिस्टरों का उपयोग करना, एक निर्देश में समानांतर में जोड़ता है जो कि स्केलर के समान तेज है addss। ( -march=nativeजो भी निर्देश आपके सीपीयू को सेट करता है उसका उपयोग करने की अनुमति देने के लिए उपयोग करें , न कि केवल SSE2 बेसलाइन x86-64 के लिए)।
पीटर कॉर्ड्स

@Cincodenada कृपया चार्ट को पूर्ण 15 को दिखाते हुए छोड़ दें क्योंकि यह प्रदर्शन का चित्रण है।
ममीज़

@PeterCordes मैं कल देखने की कोशिश करूंगा, आपके परिश्रम के लिए धन्यवाद।
म्रमीज

7

दो बिंदुओं पर विचार -

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

हमेशा की तरह, सुनिश्चित करने का एकमात्र तरीका अपने वास्तविक कार्यक्रम को प्रोफाइल करना है।

दूसरा बिंदु यह है कि इन दिनों अधिकांश सीपीयू में फ्लोटिंग पॉइंट के लिए SIMD निर्देश होते हैं जो एक ही समय में कई फ्लोटिंग पॉइंट वैल्यू पर काम कर सकते हैं। उदाहरण के लिए, आप एक ही SSE रजिस्टर में 4 फ़्लोट्स लोड कर सकते हैं और उन सभी पर 4 गुणा प्रदर्शन समानांतर में कर सकते हैं। यदि आप SSE निर्देशों का उपयोग करने के लिए अपने कोड के कुछ हिस्सों को फिर से लिख सकते हैं तो ऐसा लगता है कि यह पूर्णांक संस्करण की तुलना में तेज़ होगा। दृश्य c ++ ऐसा करने के लिए संकलक आंतरिक कार्य प्रदान करता है, कुछ जानकारी के लिए http://msdn.microsoft.com/en-us/library/x5c07e2a(v=VS.80).aspx देखें।


एक को ध्यान देना चाहिए कि Win64 पर, MSVC कंपाइलर द्वारा FPU निर्देश उत्पन्न नहीं किया जाता है। फ्लोटिंग पॉइंट हमेशा SIMD के निर्देशों का उपयोग करता है। यह फ्लॉप के बारे में Win32 और Win64 के बीच एक बड़ी गति विसंगति के लिए बनाता है।
जेम्स ड्यूने

5

यदि कोई शेष ऑपरेशन नहीं है, तो फ्लोटिंग पॉइंट संस्करण बहुत धीमा होगा। चूंकि सभी जोड़ अनुक्रमिक हैं, इसलिए सीपीयू योग को समानांतर करने में सक्षम नहीं होगा। विलंबता आलोचनात्मक होगी। FPU ऐड लेटेंसी आमतौर पर 3 चक्र है, जबकि पूर्णांक जोड़ 1 चक्र है। हालांकि, शेष ऑपरेटर के लिए विभक्त संभवतः महत्वपूर्ण हिस्सा होगा, क्योंकि यह आधुनिक सीपीयू पर पूरी तरह से पाइपलाइज्ड नहीं है। इसलिए, विभाजित / शेष निर्देश मानने से समय का बहुत अधिक उपभोग होगा, जोड़ विलंबता के कारण अंतर छोटा होगा।


4

जब तक आप कोड नहीं लिख रहे हैं, जिसे प्रति सेकंड लाखों बार कहा जाएगा (जैसे, जैसे कि ग्राफिक्स अनुप्रयोग में स्क्रीन पर एक रेखा खींचना), पूर्णांक बनाम फ्लोटिंग-पॉइंट अंकगणित शायद ही कभी अड़चन है।

दक्षता प्रश्नों का सामान्य पहला चरण यह देखने के लिए है कि रन-टाइम वास्तव में कहाँ व्यतीत होता है। इसके लिए linux कमांड है gprof

संपादित करें:

हालांकि मुझे लगता है कि आप हमेशा पूर्णांक और फ्लोटिंग-पॉइंट संख्याओं का उपयोग करके रेखा आरेखण एल्गोरिदम को लागू कर सकते हैं, इसे बड़ी संख्या में कॉल करें और देखें कि क्या इससे कोई फर्क पड़ता है:

http://en.wikipedia.org/wiki/Bresenham's_algorithm


2
वैज्ञानिक अनुप्रयोग FP का उपयोग करते हैं। एफपी का एकमात्र लाभ यह है कि परिशुद्धता स्केल-इनवेरिएंट है। यह वैज्ञानिक संकेतन की तरह है। यदि आप संख्याओं के पैमाने को पहले से ही जानते हैं (उदाहरण के लिए, कि रेखा की लंबाई पिक्सेल की एक संख्या है), तो एफपी को कम किया जाता है। लेकिन इससे पहले कि आप रेखा खींचते हैं, यह सच नहीं है।
पोटाटोस्वाटर

4

आज, पूर्णांक संचालन आमतौर पर फ्लोटिंग पॉइंट ऑपरेशंस की तुलना में थोड़ा तेज होता है। इसलिए यदि आप पूर्णांक और फ्लोटिंग पॉइंट में समान संचालन के साथ गणना कर सकते हैं, तो पूर्णांक का उपयोग करें। अब आप कह रहे हैं "यह बहुत सारी कष्टप्रद समस्याएं पैदा करता है और बहुत सारे कष्टप्रद कोड जोड़ता है"। ऐसा लगता है कि आपको अधिक संचालन की आवश्यकता है क्योंकि आप फ़्लोटिंग पॉइंट के बजाय पूर्णांक अंकगणित का उपयोग करते हैं। उस स्थिति में, फ़्लोटिंग पॉइंट तेज़ी से चलेगा क्योंकि

  • जैसे ही आपको अधिक पूर्णांक संचालन की आवश्यकता होती है, आपको संभवतः बहुत अधिक की आवश्यकता होती है, इसलिए अतिरिक्त कार्यों द्वारा खाए जाने की तुलना में थोड़ी गति का लाभ अधिक होता है

  • फ्लोटिंग-पॉइंट कोड सरल है, जिसका अर्थ है कि कोड लिखना तेज़ है, जिसका अर्थ है कि यदि यह गति महत्वपूर्ण है, तो आप कोड को अनुकूलित करने में अधिक समय व्यतीत कर सकते हैं।


यहां बहुत सारी जंगली अटकलें हैं, हार्डवेयर में मौजूद किसी भी माध्यमिक प्रभाव के लिए लेखांकन नहीं है, जो अक्सर गणना समय पर हावी होता है। एक बुरा शुरुआती बिंदु नहीं है, लेकिन इसे प्रत्येक विशेष एप्लिकेशन पर प्रोफाइलिंग के माध्यम से जांचना होगा, और सुसमाचार के रूप में नहीं पढ़ाया जाना चाहिए।
बेन वोइग्ट

3

मैंने एक परीक्षण चलाया जो सिर्फ रैंड () के बजाय संख्या में 1 जोड़ा गया। परिणाम (एक x86-64 पर) थे:

  • शॉर्ट: 4.260 एस
  • int: 4.020s
  • लंबा लंबा: 3.350s
  • फ्लोट: 7.330s
  • डबल: 7.210 s

1
स्रोत, संकलन विकल्प, और समय पद्धति? मैं परिणामों से थोड़ा हैरान हूं।
GManNickG

ओपी के रूप में "रैंड ()% 365" "1" द्वारा प्रतिस्थापित के साथ समान लूप। कोई अनुकूलन नहीं। "समय" कमांड से उपयोगकर्ता समय।
dan04

13
"कोई अनुकूलन नहीं" कुंजी है। आप अनुकूलन के साथ कभी भी प्रोफ़ाइल बंद नहीं करते, हमेशा "रिलीज़" मोड में प्रोफ़ाइल करते हैं।
डीन हार्डिंग

2
इस मामले में, हालांकि, ऑप्टिमाइज़ेशन सेशन को होने पर मजबूर करता है, और जानबूझकर किया जाता है - लूप माप के उचित पैमाने पर समय को कम करने के लिए है। निरंतर 1 का उपयोग करना रैंड () की लागत को हटा देता है। पर्याप्त रूप से स्मार्ट अनुकूलन करने वाला कंपाइलर 1 जोड़ा 100,000,000 बार देखेगा जिसमें लूप से कोई रास्ता नहीं निकलेगा और बस एक बार में 100000000 जोड़े जा सकेंगे। यह पूरे उद्देश्य के आसपास हो जाता है, है ना?
स्टेन रोजर्स

7
@Stan, वैरिएबल को अस्थिर बनाएं। फिर भी एक स्मार्ट ऑप्टिमाइज़िंग कंपाइलर को कई ऑप्स का सम्मान करना चाहिए।
vladr

0

उस ओह-सो-विश्वसनीय "कुछ मैंने सुना है" के आधार पर, पुराने दिनों में, पूर्णांक गणना लगभग 20 से 50 गुना तेज थी जो फ्लोटिंग पॉइंट थी, और इन दिनों यह दोगुनी से भी कम है।


1
कृपया इस पर विचार करने से अधिक राय देने पर विचार करें (विशेष रूप से यह देखते हुए कि राय एकत्र तथ्यों के सामने उड़ती हुई प्रतीत होती है)
MrMesees

1
@MrMesees जबकि यह उत्तर बहुत उपयोगी नहीं है, मैं कहूंगा कि यह आपके द्वारा किए गए परीक्षणों के अनुरूप है। और ऐतिहासिक सामान्य ज्ञान भी ठीक है।
जोनाथन öström 22

जैसा कि किसी ने दिन में 286 के साथ काम किया, मैं पुष्टि कर सकता हूं; "हा वे थे!"
डेविड एच पैरी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.