2017-04-21 11 views
23

का उपयोग करने से धीमा है अब तक मैं अपने प्रोजेक्ट में std::queue का उपयोग कर रहा था। मैंने औसत समय को मापा जो इस कतार पर एक विशिष्ट संचालन की आवश्यकता है।Boost.Lockfree कतार का उपयोग म्यूटेक्स

बार 2 मशीनों पर मापा गया था: मेरा स्थानीय उबंटू वीएम और रिमोट सर्वर। std::queue का उपयोग करके, औसत मशीनों पर लगभग समान था: ~ 750 माइक्रोसॉन्ड।

फिर मैंने std::queue से boost::lockfree::spsc_queue पर "अपग्रेड किया", इसलिए मैं कतार की रक्षा करने वाले म्यूटेक्स से छुटकारा पा सकता हूं। मेरे स्थानीय वीएम पर मैं एक बड़ा प्रदर्शन लाभ देख सकता था, औसत अब 200 माइक्रोसॉन्ड पर है। रिमोट मशीन पर हालांकि, औसत 800 माइक्रोसॉन्ड तक चला गया, जो इससे पहले धीमा है।

सबसे पहले मैंने सोचा था कि इस वजह से रिमोट मशीन ताला मुक्त कार्यान्वयन का समर्थन नहीं कर सकते हैं हो सकता है:

Boost.Lockfree page:

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

यह जानने के लिए इन निर्देशों का समर्थन कर रहे, boost::lockfree::queue एक विधि bool is_lock_free(void) const; कहा जाता है। हालांकि, boost::lockfree::spsc_queue में ऐसा कोई फ़ंक्शन नहीं है, जो मेरे लिए, इसका तात्पर्य है कि यह हार्डवेयर पर भरोसा नहीं करता है और यह किसी भी मशीन पर हमेशा लॉकफ्री होता है।

प्रदर्शन हानि का कारण क्या हो सकता है?


exmple कोड (निर्माता/उपभोक्ता)

// c++11 compiler and boost library required 

#include <iostream> 
#include <cstdlib> 
#include <chrono> 
#include <async> 
#include <thread> 
/* Using blocking queue: 
* #include <mutex> 
* #include <queue> 
*/ 
#include <boost/lockfree/spsc_queue.hpp> 


boost::lockfree::spsc_queue<int, boost::lockfree::capacity<1024>> queue; 

/* Using blocking queue: 
* std::queue<int> queue; 
* std::mutex mutex; 
*/ 

int main() 
{ 
    auto producer = std::async(std::launch::async, [queue /*,mutex*/]() 
    { 
     // Producing data in a random interval 
     while(true) 
     { 
      /* Using the blocking queue, the mutex must be locked here. 
      * mutex.lock(); 
      */ 

      // Push random int (0-9999) 
      queue.push(std::rand() % 10000); 

      /* Using the blocking queue, the mutex must be unlocked here. 
      * mutex.unlock(); 
      */ 

      // Sleep for random duration (0-999 microseconds) 
      std::this_thread::sleep_for(std::chrono::microseconds(rand() % 1000)); 
     } 
    } 

    auto consumer = std::async(std::launch::async, [queue /*,mutex*/]() 
    { 
     // Example operation on the queue. 
     // Checks if 1234 was generated by the producer, returns if found. 

     while(true) 
     { 
      /* Using the blocking queue, the mutex must be locked here. 
      * mutex.lock(); 
      */ 

      int value; 
      while(queue.pop(value) 
      { 
       if(value == 1234) 
        return; 
      } 

      /* Using the blocking queue, the mutex must be unlocked here. 
      * mutex.unlock(); 
      */ 

      // Sleep for 100 microseconds 
      std::this_thread::sleep_for(std::chrono::microseconds(100)); 
     } 
    } 

    consumer.get(); 
    std::cout << "1234 was generated!" << std::endl; 
    return 0; 
} 
+1

कृपया एक [mcve] जोड़ने पर विचार करें जो आपके प्रदर्शन माप को पुन: पेश करने की अनुमति देता है। इससे अधिक व्यावहारिक उत्तर की अनुमति मिल जाएगी। – Zulan

+0

इस प्रश्न में उच्च रुचि को देखते हुए, यह वास्तव में दुर्भाग्यपूर्ण है कि दो अलग-अलग प्रणालियों पर प्रदर्शन विसंगति के मूल पहलू का उत्तर नहीं दिया जा सकता है। मुझे लगता है कि प्रश्न में सुधार होने पर एक विशिष्ट व्यावहारिक उत्तर के लिए और अधिक संभावनाएं हैं। – Zulan

+0

@Zulan मैं जल्द ही एक ठोस उदाहरण जोड़ने की कोशिश करूंगा। – Bobface

उत्तर

52

लॉक मुक्त एल्गोरिदम आम तौर पर ताला आधारित एल्गोरिथम की तुलना में पहले से खराब प्रदर्शन। यह एक महत्वपूर्ण कारण है जिसका उपयोग लगभग उतनी बार नहीं किया जाता है।

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

मुझे आपको एक बहुत चरम काल्पनिक प्रदान करने दें। एक सामान्य, आधुनिक दोहरे कोर सीपीयू पर चार धागे चल रहे हैं कल्पना कीजिए। थ्रेड ए 1 और ए 2 संग्रह ए में छेड़छाड़ कर रहे हैं। थ्रेड बी 1 और बी 2 संग्रह बी में हैं।

सबसे पहले, कल्पना करें कि संग्रह ताले का उपयोग करता है। इसका मतलब यह होगा कि यदि थ्रेड ए 1 और ए 2 (या बी 1 और बी 2) एक ही समय में चलाने की कोशिश करते हैं, तो उनमें से एक लॉक द्वारा अवरुद्ध हो जाएगा। तो, बहुत जल्दी, एक धागा और एक बी धागा चल रहा होगा। ये धागे बहुत जल्दी चले जाएंगे और विरोध नहीं करेंगे। किसी भी समय धागे का विरोध करने की कोशिश करते हैं, विरोधाभासी धागा डी-निर्धारित हो जाएगा। वाह।

अब, कल्पना करें कि संग्रह कोई ताले का उपयोग नहीं करता है। अब, एक ही समय में धागे ए 1 और ए 2 चल सकते हैं। यह निरंतर विवाद पैदा करेगा। संग्रह के लिए कैश लाइनों दो कोर के बीच पिंग-पोंग होगा। इंटर-कोर बसें संतृप्त हो सकती हैं। प्रदर्शन भयानक होगा।

फिर से, यह बेहद अतिरंजित है। लेकिन आप विचार समझ गये। आप विवाद से बचना चाहते हैं, जितना संभव हो उतना पीड़ित नहीं।

हालांकि, अब इस विचार प्रयोग को फिर से चलाएं जहां ए 1 और ए 2 पूरे सिस्टम पर एकमात्र धागे हैं। अब, लॉक फ्री संग्रह शायद बेहतर है (हालांकि आप पाते हैं कि उस मामले में केवल एक धागा होना बेहतर है!)।

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

+7

बहुत अच्छा, कैनोलिक जवाब। जब समय खिड़की गुजरती है तो मैं इसे प्रतिबिंबित करूंगा। – sehe

+1

हम्म मुझे लगता है कि मैं वर्तमान में उसी चरण में हूं जहां मैं ताले से बचने के लिए प्रवृत्त हूं: पी, क्या कोई अन्य संसाधन/पुस्तक है जिसे मैं पढ़ सकता हूं जहां ऐसे विषयों को छुआ है? –

+0

@AbhinavGauniyal मुझे उससे पूछा जाता है, और मुझे अभी तक एक नहीं मिला है। मैंने कई लोगों से बात की है जो एक ही दर्दनाक प्रक्रिया से गुजर चुके हैं जो मैंने कई साल पहले किया था। –

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