2012-10-02 30 views
7

मुझे पता है कि मानक स्टड डेक वास्तव में धीमा है जब स्टैक के "घर से बने" संस्करण की तुलना में पूर्व-आवंटित सरणी का उपयोग किया जाता है।std deque आश्चर्यजनक रूप से धीमा है

template <class T> 
class FastStack 
{  
public: 
    T* st; 
    int allocationSize; 
    int lastIndex; 

public: 
    FastStack(int stackSize); 
    FastStack(); 
    ~FastStack(); 

    inline void resize(int newSize); 
    inline void push(T x); 
    inline void pop(); 
    inline T getAndRemove(); 
    inline T getLast(); 
    inline void clear(); 
}; 

template <class T> 
FastStack<T>::FastStack() 
{ 
    lastIndex = -1; 
    st = NULL; 
} 

template <class T> 
FastStack<T>::FastStack(int stackSize) 
{ 
    st = NULL; 
    this->allocationSize = stackSize; 
    st = new T[stackSize]; 
    lastIndex = -1; 
} 

template <class T> 
FastStack<T>::~FastStack() 
{ 
    delete [] st; 
} 

template <class T> 
void FastStack<T>::clear() 
{ 
    lastIndex = -1; 
} 

template <class T> 
T FastStack<T>::getLast() 
{ 
    return st[lastIndex]; 
} 

template <class T> 
T FastStack<T>::getAndRemove() 
{ 
    return st[lastIndex--]; 
} 

template <class T> 
void FastStack<T>::pop() 
{ 
    --lastIndex; 
} 

template <class T> 
void FastStack<T>::push(T x) 
{ 
    st[++lastIndex] = x; 
} 

template <class T> 
void FastStack<T>::resize(int newSize) 
{ 
    if (st != NULL) 
     delete [] st; 
    st = new T[newSize]; 
} 

:
यह मेरा ढेर के कोड है।
मैं Deque के लिए इस सरल बेंचमार्क चलाएँ:

std::deque<int> aStack; 
int x; 

HRTimer timer; 
timer.Start(); 

for (int i = 0; i < 2000000000; i++) 
{ 
    aStack.push_back(i); 
    x = aStack.back(); 
    if (i % 100 == 0 && i != 0) 
     for (int j = 0; j < 100; j++) 
      aStack.pop_back(); 
} 

double totalTime = timer.Stop(); 
stringstream ss; 
ss << "std::deque " << totalTime; 
log(ss.str()); 


कंटेनर के रूप में वेक्टर के साथ एसटीडी ढेर (के रूप में माइकल Kohne 'का सुझाव दिया)

std::stack<int, std::vector<int>> bStack; 
int x; 

HRTimer timer; 
timer.Start(); 

for (int i = 0; i < 2000000000; i++) 
{ 
    bStack.push(i); 
    x = bStack.top(); 
    if (i % 100 == 0 && i != 0) 
     for (int j = 0; j < 100; j++) 
      bStack.pop(); 
} 

double totalTime = timer.Stop(); 
stringstream ss; 
ss << "std::stack " << totalTime; 
log(ss.str()); 

का उपयोग करना।
और मेरे FastStack के लिए एक ही एक:

FastStack<int> fstack(200); 
int x; 

HRTimer timer; 
timer.Start(); 

for (int i = 0; i < 2000000000; i++) 
{ 
    fstack.push(i); 
    x = fstack.getLast(); 
    if (i % 100 == 0 && i != 0) 
     for (int j = 0; j < 100; j++) 
      fstack.pop(); 
} 

double totalTime = timer.Stop(); 
stringstream ss; 
ss << "FastStack " << totalTime; 
log(ss.str()); 


4 रन के बाद परिणाम इस प्रकार हैं:
Deque 15.529
Deque 15.3756
Deque 15.429
Deque 15,4778

ढेर ६.१९०९९
ढेर 6.1834
ढेर ६.१९३१५
ढेर 6,19841

फास्टस्टैक 3.01085
FastStack 2.9934
FastStack ३.०२५३६
FastStack 3,00937

परिणाम सेकंड में कर रहे हैं और मैं इंटेल i5 3570k पर कोड (डिफ़ॉल्ट घड़ी पर) चल रहा था। मैंने वीएस -2010 कंपाइलर का उपयोग उन सभी अनुकूलन के साथ किया जो उपलब्ध हैं। मुझे पता है कि मेरे फास्टस्टैक में बहुत सी सीमाएं हैं लेकिन बहुत सी स्थितियां हैं, जहां इसका उपयोग किया जा सकता है और जब यह अच्छा बढ़ावा दे सकता है! (मैंने इसे एक प्रोजेक्ट में इस्तेमाल किया जहां मुझे std :: queue की तुलना में 2x गति मिलती है)।
तो अब मेरा प्रश्न है:
क्या सी ++ में कोई अन्य "अवरोधक" है जो हर कोई उपयोग करता है लेकिन कोई भी उनके बारे में नहीं जानता?
संपादित करें: मैं आक्रामक नहीं होना चाहता, अगर आप कुछ अज्ञात "speedups" जानते हैं तो मैं उत्सुक हूं।

+0

इस तरह "अवरोधक" की एक सूची के बारे में पूछना रचनात्मक नहीं है। – nhahtdh

+2

डेक बनाने के बाद, उस पर [आकार बदलें] (http://www.cplusplus.com/reference/stl/deque/resize/) पर कॉल करें। देखें कि क्या यह एक बड़ा गति अंतर बनाता है। मेरा अनुमान है कि आकार को बदलकर स्मृति को "स्मार्टली" प्रबंधित करने की कोशिश कर रहे डेक क्लास से प्राप्त गति/खोई गई गति का एक बड़ा हिस्सा है। –

+0

आप अपने कथन पर ब्रेसिज़ खो रहे हैं। – Rapptz

उत्तर

14

यदि आप किसी भी समय किसी भी समय स्टैक में मौजूद तत्वों की अधिकतम मात्रा जानते हैं तो आपको std::vector का उपयोग करना चाहिए जिस पर आप क्षमता को पहले से सुरक्षित रखते हैं। यहां तक ​​कि यदि आप क्षमता को नहीं जानते हैं, तो स्टैक के लिए आपको std::vector का उपयोग करना चाहिए जो कुछ बार बढ़ेगा (बढ़ते समय एक प्रीलोकेटेड वेक्टर की तुलना में उच्च लागत) लेकिन कभी भी कम नहीं होता है, इसलिए कुछ समय बाद यह स्मृति आवंटित करना बंद कर देगा।

प्रदर्शन के साथ इस मुद्दे, जब वे अब जरूरत है (कुछ रणनीति का पालन) हैं कि std::deque रूप में की जरूरत ब्लॉक आवंटित करेगा, और उन्हें पुनःआवंटन है, इसलिए यदि आप को भरने और लगातार std::deque आवंटन और deallocations अक्सर नहीं होगा साफ़ करें।

+0

आप सही हैं। प्रदर्शन 15 से 6 एस तक बेहतर हो जाता है लेकिन जब भी मैं बनाता हूं, सरल 'स्टैक' कक्षा की तुलना में यह वास्तव में धीमा है। मुझे पता है कि मेरे संस्करण में कोई त्रुटि हैंडलिंग या अन्य सुविधाएं नहीं हैं लेकिन यह तेज़ है और सार्वजनिक सदस्यों के कारण यादृच्छिक पहुंच भी देता है ... – icl7126

+2

@klerik: आप किस कंपाइलर और झंडे का उपयोग कर रहे हैं? एकमात्र कारण 'std :: vector' आपके हैंडकार्ड कोड से धीमा होगा यदि आप चेक किए गए इटरेटर्स का उपयोग कर रहे हैं, या डीबग मोड में संकलित नहीं करते हैं, जिसमें कोई फ़ंक्शन इनलाइन नहीं है। प्रदर्शन परीक्षण तुच्छ नहीं है, आपको वास्तव में समझना होगा कि आप क्या कर रहे हैं और परीक्षण के प्रभाव या अन्यथा आप परिणामों को उचित रूप से समझने में सक्षम नहीं होंगे। –

+0

मैं वीएस -2010 का उपयोग कर रहा था, सभी मानदंड पैरामीटर/ओ 2,/ओई,/ओटी,/ओई-,/जीएल और/ओबी 2 के साथ रिलीज बिल्ड पर चल रहे थे, इसलिए कोई भी उपयुक्त फ़ंक्शन इनलाइन होना चाहिए और सभी कोड अनुकूलन कार्य करना चाहिए। मैं केवल स्टैक क्षमता का पता लगाने के लिए डीबग का उपयोग कर रहा था। लेकिन मैं चेक इटरेटर्स का उपयोग कर रहा था इसलिए मैंने इसे अभी बंद कर दिया लेकिन ऐसा लगता है कि इससे कोई फर्क नहीं पड़ता। – icl7126

21

आप सेब और संतरे की तुलना कर रहे हैं। डेक्यू डब्लूबल-एंडेड है, जिसे आपके द्वारा लागू किए गए सरल स्टैक की तुलना में आंतरिक रूप से थोड़ा अलग होना आवश्यक है। इसके बजाय std::stack<int, std::vector<int> > के विरुद्ध अपना बेंचमार्क चलाने का प्रयास करें और देखें कि आप कैसे करते हैं। std :: स्टैक एक कंटेनर एडाप्टर है, और यदि आप अंतर्निहित कंटेनर के रूप में वेक्टर का उपयोग करते हैं, तो यह आपके घर के उगाए जाने वाले कार्यान्वयन के जितना तेज़ होना चाहिए।

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

+0

स्टैक के लिए वेक्टर के रूप में वेक्टर का उपयोग करके समान बेंचमार्क : 6.190 99 6.1834 6.19315 6.19841। बेंचमार्क एक ही समय में केवल 100 नंबरों को ढेर में रखता है, इसलिए पुनर्वितरण इतना बड़ी समस्या नहीं होनी चाहिए। – icl7126

+0

तो std :: स्टैक (आकार सेट करने के लिए) के साथ एक पुनरावृत्ति करें और _then_ टाइम लूप (उसी पूर्व-विकसित स्टैक का उपयोग करके) करें। यह अब कैसे दिखता है? – Useless

+0

ठीक है तो मैंने एक लूप किया और 100 नंबर जोड़ दिया, फिर एक और पाश और उन्हें हटाएं (लाइन 'एचआरटीमर टाइमर' से पहले)। डीबगर का उपयोग करके मैं इन लूपों के बाद स्टैक क्षमता की जांच करता हूं और यह 141 था और हर समय 141 रहता है (क्योंकि केवल 100 तत्व एक ही समय में जोड़े जाते हैं) और परिणाम खराब होते हैं - 6.65823। मैंने कुछ प्रयोग किए हैं इसलिए मैं 1000 तक जाने के लिए लूप बदलता हूं और फिर परिणाम 6.34 था और फिर मैंने इसे 1000000 में बदल दिया और परिणाम 6.35 (क्षमता 104986 9 थी)। मुझे नहीं पता कि अप्रयुक्त क्षमता स्थान बदलते समय परिणाम क्यों बदल रहा है (मेरा मतलब है 100 और 1000 के बीच का अंतर)। – icl7126