2014-11-11 20 views
42

से std :: जोड़ी तेज क्यों है परीक्षण के लिए कोड यहां दिया गया है।std :: tuple

टपल परीक्षण:

using namespace std; 

int main(){ 

    vector<tuple<int,int>> v; 


    for (int var = 0; var < 100000000; ++var) { 
     v.push_back(make_tuple(var, var)); 
    } 
} 

जोड़ी परीक्षण:

#include <vector> 

using namespace std; 

int main(){ 

    vector<pair<int,int>> v; 


    for (int var = 0; var < 100000000; ++var) { 
     v.push_back(make_pair(var, var)); 
    } 
} 

मैं लिनक्स समय आदेश के माध्यम से समय माप किया था। परिणाम हैं:

|  | -O0 | -O2 | 
|:------|:-------:|:--------:| 
| Pair | 8.9 s | 1.60 s | 
| Tuple | 19.8 s | 1.96 s | 

मैं सोच रहा हूँ, क्यों O0 में उन दो डाटा संरचनाओं के बीच इस तरह के एक बड़ा अंतर है, क्योंकि वे बहुत समान होना चाहिए। 02.

में बहुत छोटा अंतर है ओ0 में इतना अंतर क्यों है, और इसमें कोई अंतर क्यों है?

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

v.resize साथ कोड()

जोड़ी:

#include <vector> 

using namespace std; 

int main(){ 

    vector<pair<int,int>> v; 

    v.resize(100000000); 

    for (int var = 0; var < 100000000; ++var) { 
     v[var] = make_pair(var, var); 
    } 
} 

टपल:

#include<tuple> 
#include<vector> 

using namespace std; 

int main(){ 

    vector<tuple<int,int>> v; 

    v.resize(100000000); 

    for (int var = 0; var < 100000000; ++var) { 
     v[var] = make_tuple(var, var); 
    } 
} 

परिणाम:

|  | -O0 | -O2 | 
|:------|:-------:|:--------:| 
| Pair | 5.01 s | 0.77 s | 
| Tuple | 10.6 s | 0.87 s | 

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

मेरे प्रणाली

g++ (GCC) 4.8.3 20140911 (Red Hat 4.8.3-7) 
GLIBCXX_3.4.19 
+0

आप कॉल स्टैक और 'आकार' क्यों नहीं देखते हैं? – Ajay

+16

मैं कहूंगा कि आपको 'v.reserve (100000000) करना चाहिए;' दोनों मामले में लूप से पहले इसे एक और सटीक परीक्षण करने के लिए। –

+0

@ जोनाथनपोटर लेकिन यदि यह अंतर हल करता है, तो यह दिखाएगा कि निर्माण धीमा है। – gbjbaanb

उत्तर

61

आप कुछ महत्वपूर्ण जानकारी याद कर रहे हैं: आप क्या संकलक प्रयोग करते हैं? माइक्रोबेंमार्क के प्रदर्शन को मापने के लिए आप क्या उपयोग करते हैं? आप किस मानक पुस्तकालय कार्यान्वयन का उपयोग करते हैं?

मेरे सिस्टम:

g++ (GCC) 4.9.1 20140903 (prerelease) 
GLIBCXX_3.4.20 

किसी भी तरह, मैं अपने उदाहरण भाग गया, लेकिन वैक्टर पहले स्मृति आवंटन भूमि के ऊपर से छुटकारा पाने के की उचित आकार सुरक्षित। कि के साथ, मैं मजेदार विपरीत कुछ दिलचस्प निरीक्षण - रिवर्स आप जो देखते हैं की:

g++ -std=c++11 -O2 pair.cpp -o pair 
perf stat -r 10 -d ./pair 
Performance counter stats for './pair' (10 runs): 

     1647.045151  task-clock:HG (msec)  # 0.993 CPUs utilized   (+- 1.94%) 
       346  context-switches:HG  # 0.210 K/sec     (+- 40.13%) 
       7  cpu-migrations:HG   # 0.004 K/sec     (+- 22.01%) 
      182,978  page-faults:HG   # 0.111 M/sec     (+- 0.04%) 
    3,394,685,602  cycles:HG     # 2.061 GHz      (+- 2.24%) [44.38%] 
    2,478,474,676  stalled-cycles-frontend:HG # 73.01% frontend cycles idle  (+- 1.24%) [44.55%] 
    1,550,747,174  stalled-cycles-backend:HG # 45.68% backend cycles idle  (+- 1.60%) [44.66%] 
    2,837,484,461  instructions:HG   # 0.84 insns per cycle   
                # 0.87 stalled cycles per insn (+- 4.86%) [55.78%] 
     526,077,681  branches:HG    # 319.407 M/sec     (+- 4.52%) [55.82%] 
      829,623  branch-misses:HG   # 0.16% of all branches   (+- 4.42%) [55.74%] 
     594,396,822  L1-dcache-loads:HG  # 360.887 M/sec     (+- 4.74%) [55.59%] 
     20,842,113  L1-dcache-load-misses:HG # 3.51% of all L1-dcache hits (+- 0.68%) [55.46%] 
     5,474,166  LLC-loads:HG    # 3.324 M/sec     (+- 1.81%) [44.23%] 
    <not supported>  LLC-load-misses:HG  

     1.658671368 seconds time elapsed           (+- 1.82%) 

बनाम:

g++ -std=c++11 -O2 tuple.cpp -o tuple 
perf stat -r 10 -d ./tuple 
Performance counter stats for './tuple' (10 runs): 

     996.090514  task-clock:HG (msec)  # 0.996 CPUs utilized   (+- 2.41%) 
       102  context-switches:HG  # 0.102 K/sec     (+- 64.61%) 
       4  cpu-migrations:HG   # 0.004 K/sec     (+- 32.24%) 
      181,701  page-faults:HG   # 0.182 M/sec     (+- 0.06%) 
    2,052,505,223  cycles:HG     # 2.061 GHz      (+- 2.22%) [44.45%] 
    1,212,930,513  stalled-cycles-frontend:HG # 59.10% frontend cycles idle  (+- 2.94%) [44.56%] 
     621,104,447  stalled-cycles-backend:HG # 30.26% backend cycles idle  (+- 3.48%) [44.69%] 
    2,700,410,991  instructions:HG   # 1.32 insns per cycle   
                # 0.45 stalled cycles per insn (+- 1.66%) [55.94%] 
     486,476,408  branches:HG    # 488.386 M/sec     (+- 1.70%) [55.96%] 
      959,651  branch-misses:HG   # 0.20% of all branches   (+- 4.78%) [55.82%] 
     547,000,119  L1-dcache-loads:HG  # 549.147 M/sec     (+- 2.19%) [55.67%] 
     21,540,926  L1-dcache-load-misses:HG # 3.94% of all L1-dcache hits (+- 2.73%) [55.43%] 
     5,751,650  LLC-loads:HG    # 5.774 M/sec     (+- 3.60%) [44.21%] 
    <not supported>  LLC-load-misses:HG  

     1.000126894 seconds time elapsed           (+- 2.47%) 

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

अब यह कहां से आया है?मुझे यकीन है कि यह आता है करने के लिए नीचे कुछ इनलाइनिंग, के लिए इसी तरह विफल रहा है क्या यहाँ समझाया गया है: दरअसल std::vector performance regression when enabling C++11

, सक्रिय करने के -flto equalizes मेरे लिए परिणाम:

Performance counter stats for './pair' (10 runs): 

     1021.922944  task-clock:HG (msec)  # 0.997 CPUs utilized   (+- 1.15%) 
       63  context-switches:HG  # 0.062 K/sec     (+- 77.23%) 
       5  cpu-migrations:HG   # 0.005 K/sec     (+- 34.21%) 
      195,396  page-faults:HG   # 0.191 M/sec     (+- 0.00%) 
    2,109,877,147  cycles:HG     # 2.065 GHz      (+- 0.92%) [44.33%] 
    1,098,031,078  stalled-cycles-frontend:HG # 52.04% frontend cycles idle  (+- 0.93%) [44.46%] 
     701,553,535  stalled-cycles-backend:HG # 33.25% backend cycles idle  (+- 1.09%) [44.68%] 
    3,288,420,630  instructions:HG   # 1.56 insns per cycle   
                # 0.33 stalled cycles per insn (+- 0.88%) [55.89%] 
     672,941,736  branches:HG    # 658.505 M/sec     (+- 0.80%) [56.00%] 
      660,278  branch-misses:HG   # 0.10% of all branches   (+- 2.05%) [55.93%] 
     474,314,267  L1-dcache-loads:HG  # 464.139 M/sec     (+- 1.32%) [55.73%] 
     19,481,787  L1-dcache-load-misses:HG # 4.11% of all L1-dcache hits (+- 0.80%) [55.51%] 
     5,155,678  LLC-loads:HG    # 5.045 M/sec     (+- 1.69%) [44.21%] 
    <not supported>  LLC-load-misses:HG  

     1.025083895 seconds time elapsed           (+- 1.03%) 

और टपल के लिए:

Performance counter stats for './tuple' (10 runs): 

     1018.980969  task-clock:HG (msec)  # 0.999 CPUs utilized   (+- 0.47%) 
       8  context-switches:HG  # 0.008 K/sec     (+- 29.74%) 
       3  cpu-migrations:HG   # 0.003 K/sec     (+- 42.64%) 
      195,396  page-faults:HG   # 0.192 M/sec     (+- 0.00%) 
    2,103,574,740  cycles:HG     # 2.064 GHz      (+- 0.30%) [44.28%] 
    1,088,827,212  stalled-cycles-frontend:HG # 51.76% frontend cycles idle  (+- 0.47%) [44.56%] 
     697,438,071  stalled-cycles-backend:HG # 33.15% backend cycles idle  (+- 0.41%) [44.76%] 
    3,305,631,646  instructions:HG   # 1.57 insns per cycle   
                # 0.33 stalled cycles per insn (+- 0.21%) [55.94%] 
     675,175,757  branches:HG    # 662.599 M/sec     (+- 0.16%) [56.02%] 
      656,205  branch-misses:HG   # 0.10% of all branches   (+- 0.98%) [55.93%] 
     475,532,976  L1-dcache-loads:HG  # 466.675 M/sec     (+- 0.13%) [55.69%] 
     19,430,992  L1-dcache-load-misses:HG # 4.09% of all L1-dcache hits (+- 0.20%) [55.49%] 
     5,161,624  LLC-loads:HG    # 5.065 M/sec     (+- 0.47%) [44.14%] 
    <not supported>  LLC-load-misses:HG  

     1.020225388 seconds time elapsed           (+- 0.48%) 

तो याद रखें, -flto आपका मित्र है और असफल इनलाइनिंग में अत्यधिक टेम्पलेट कोड पर अत्यधिक परिणाम हो सकते हैं। क्या हो रहा है यह जानने के लिए perf stat का उपयोग करें।

+0

पीएस: मुझे लगता है कि जोड़ी धीमी है क्योंकि इसे शायद टुपल का उपयोग करके लागू किया गया है और बस इनलाइन गहराई सीमा को हिट करने के लिए होता है। – milianw

+13

गलत धारणा। 'std :: pair' के पास 'पहले' और 'second'' नामक दो सच्चे डेटा सदस्यों की आवश्यकता होती है, इसलिए इसे किसी और चीज के संदर्भ में लागू नहीं किया जा सकता है। –

+2

'जोड़ी' को सी ++ में 'tuple' दिखाई देने से पहले C++ में पेश किया गया था, इसलिए इसे टुपल द्वारा कार्यान्वित नहीं किया जा सकता है। इसके अलावा, जो भी एक साधारण के लिए एक और जटिल संरचना का उपयोग करते हैं? –

34

मिलिअन -O0 बनाम -O2 को संबोधित नहीं किया, इसलिए मैं इसके लिए स्पष्टीकरण जोड़ना चाहता हूं।

यह पूरी तरह से उम्मीद है कि std::tuplestd::pair धीमी गति से हो जाएगा जब नहीं अनुकूलित किया है, क्योंकि यह और अधिक जटिल वस्तु है। एक जोड़ी में वास्तव में दो सदस्य होते हैं, इसलिए इसकी विधियां परिभाषित करने के लिए सरल होती हैं। लेकिन tuple के सदस्यों की मनमानी संख्या है और टेम्पलेट तर्क सूची पर फिर से शुरू करने का एकमात्र तरीका रिकर्सन के साथ है। इसलिए टुपल के लिए अधिकांश फ़ंक्शन एक सदस्य को संभालते हैं और फिर बाकी को संभालने के लिए पुन: प्रयास करते हैं, इसलिए 2-टुपल के लिए आपके पास दो बार फ़ंक्शन कॉल होते हैं।

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

+0

मैंने कारण की एक ही पंक्ति (तर्क की मनमानी संख्या) के साथ उत्तर दिया होगा, तो मैंने देखा कि std :: tuple के जोड़े के लिए अतिरिक्त कन्स्ट्रक्टर है (http: // www.cplusplus।कॉम/संदर्भ/tuple/tuple/tuple/(5)), जो तर्क सूची पुनरावृत्ति को रोकना चाहिए। –

+6

@ सोरीनोटूअरेज़: उस कन्स्ट्रक्टर का उपयोग यहां नहीं किया जा रहा है। और यहां तक ​​कि यह रिकर्सन से बच नहीं सकता क्योंकि टुपल की संरचना खुद रिकर्सिव है। –

+0

क्या यह भाषा द्वारा आवश्यक है? मैंने सोचा होगा कि मानक में 'std :: tuple' _specified_ हो सकता है लेकिन एक कार्यान्वयन जो भी चाहता है वह कर सकता है। उदा।, सभी सामान्य (<9 तत्व) ट्यूपल आकारों के लिए विशेषज्ञता है। – davidbak

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