2009-01-16 31 views
22

अद्यतन: यहां my implementation of Hashed Timing Wheels है। यदि आपको प्रदर्शन और समरूपता में सुधार करने का कोई विचार है तो कृपया मुझे बताएं। (20-जनवरी-2009)एक प्राथमिकता कतार जो कुशल प्राथमिकता अद्यतन की अनुमति देता है?

// Sample usage: 
public static void main(String[] args) throws Exception { 
    Timer timer = new HashedWheelTimer(); 
    for (int i = 0; i < 100000; i ++) { 
     timer.newTimeout(new TimerTask() { 
      public void run(Timeout timeout) throws Exception { 
       // Extend another second. 
       timeout.extend(); 
      } 
     }, 1000, TimeUnit.MILLISECONDS); 
    } 
} 

अद्यतन: मैं Hierarchical and Hashed Timing Wheels का उपयोग कर इस समस्या का समाधान। (1 9-जनवरी -2009)

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

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

मैं java.util.Timer या java.util.concurrent का उपयोग नहीं कर सकता। अनुसूचित थ्रेडपूल एक्स्सेलर क्योंकि वे मानते हैं कि अधिकांश कार्यों का समय समाप्त होना चाहिए। यदि कोई कार्य रद्द कर दिया गया है, तो रद्द किए गए कार्य को अपने आंतरिक ढेर में तब तक संग्रहीत किया जाता है जब तक शेड्यूल किए गए थ्रेडपूलएक्ससेलर.पुर्ज() को कॉल नहीं किया जाता है, और यह एक बहुत महंगा ऑपरेशन है। (ओ (एनएलएलएन) शायद?)

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

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

+1

ओ (लॉगएन) अपडेट क्यों धीमा हो जाएगा? – ConcernedOfTunbridgeWells

+0

क्योंकि अद्यतन बहुत बार होता है। मान लीजिए कि हम प्रति कनेक्शन एम संदेश भेज रहे हैं तो समग्र समय ओ (एमएनएलओएनएन) बन जाता है, जो कि काफी बड़ा है। – trustin

+0

मैं आपकी समस्या को स्पष्ट रूप से समझ नहीं पाया। क्या आप फिर से बदल सकते हैं? – user51568

उत्तर

13

सामान्य मामले को संभालने की कोशिश करने के तरीके के बारे में जहां त्रुटि मामलों से चीजें जल्दी से पूरी होती हैं?

हैश तालिका और प्राथमिकता कतार दोनों का उपयोग करें। जब कोई कार्य शुरू होता है तो इसे हैश टेबल में डाल दिया जाता है और यदि यह जल्दी खत्म हो जाता है तो यह ओ (1) समय में हटा दिया जाता है।

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

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

विकल्प प्राथमिकता कतार का उपयोग करने से कहीं अधिक जटिल हैं, लेकिन बहुत आसानी से कार्यान्वित किया जाना चाहिए स्थिर होना चाहिए।

+2

यह बहुत समझ में आता है कि इसमें हैश टेबल के साथ अधिकांश मामलों को शामिल किया गया है ताकि प्रति संदेश अपडेट/रद्दीकरण समय ओ (1) हो। यदि सामान्य प्रतिक्रिया समय सीमा 0 से 60 सेकंड तक फैली हुई है, तो मैं अधिक हैश टेबल बना सकता हूं। धन्यवाद! – trustin

0

आपको कतार में वस्तुओं की संख्या पर हार्ड-सीमा मिली है - टीसीपी सॉकेट की सीमा है।

इसलिए समस्या बाध्य है। मुझे संदेह है कि किसी भी चालाक डेटा संरचना अंतर्निहित प्रकारों का उपयोग करने से धीमी हो जाएगी।

+1

एक क्लाइंट एप्लिकेशन में बंदरगाहों की संख्या के कारण 64k कनेक्शन की सीमा है, लेकिन एक सर्वर साइड एप्लिकेशन उस समय से अधिक संभाल सकता है जब तक इसमें पर्याप्त CPU पावर हो। और यहां तक ​​कि एक कनेक्शन प्रत्येक के लिए टाइमआउट सेट करने के लिए 10k संदेश/सेकंड भेज सकता है। – trustin

0

क्या java.lang.PriorityQueue का उपयोग न करने का कोई अच्छा कारण नहीं है? लॉग (एन) समय में अपने रद्द संचालन को हटा नहीं है()? फिर कतार के मोर्चे पर आइटम तक उस समय के आधार पर अपना इंतजार लागू करें।

+0

लॉग (एन) पर्याप्त तेज़ नहीं है। प्रत्येक कनेक्शन पर विचार करें कि संदेशों को प्रत्येक के लिए जितनी जल्दी संभव सेटिंग टाइमआउट भेजना है। – trustin

+3

java.util.PriorityQueue # निकालें (ऑब्जेक्ट) ओ (एन) है, ओ नहीं (लॉगएन)! –

0

मुझे लगता है कि सभी कार्यों को एक सूची में संग्रहित करना और उनके माध्यम से पुनरावृत्ति करना सबसे अच्छा होगा।

आपको कुछ सुंदर बीफ़ी मशीन पर सर्वर चलाने के लिए (चलने) होना चाहिए, जहां सीमाएं इस सीमा के लिए महत्वपूर्ण होंगी?

+0

मैं वास्तव में एक जेनेरिक नेटवर्क एप्लिकेशन फ्रेमवर्क [लिंक टेक्स्ट] [1] लिख रहा हूं, इसलिए इसे कमोडिटी हार्डवेयर और बीफ़ी मशीन पर दोनों को ठीक से चलाने की जरूरत है। [1]: http://www.jboss.org/netty/ – trustin

5

हैश और ओ (लॉगएन) संरचनाओं के कुछ संयोजन आपको जो करना चाहिए वह करना चाहिए।

मैं इस समस्या का विश्लेषण करने के तरीके से छेड़छाड़ करने के लिए प्रेरित हूं। उपरोक्त आपकी टिप्पणी में, आप

क्योंकि अद्यतन बहुत बार होता है। मान लीजिए कि हम प्रति कनेक्शन एम संदेश भेज रहे हैं तो समग्र समय ओ (एमएनएलओएनएन) बन जाता है, जो कि काफी बड़ा है। - ट्रस्टिन ली (6 घंटे पूर्व)

जो कि जहां तक ​​जाता है बिल्कुल सही है। लेकिन मुझे पता है कि ज्यादातर लोग प्रति संदेश पर ध्यान केंद्रित करेंगे, इस सिद्धांत पर कि आपके ऐप में अधिक से अधिक काम करने के लिए, स्पष्ट रूप से इसे अधिक संसाधनों की आवश्यकता होगी।

तो यदि आपके आवेदन में अरबों सॉकेट खुले हैं (क्या वास्तव में यह संभव है?) प्रविष्टि लागत प्रति संदेश केवल 60 तुलना है।

मैं शर्त लगाता हूं कि यह समयपूर्व अनुकूलन है: आपने वास्तव में कोडएनलिस्ट या वीट्यून जैसे प्रदर्शन विश्लेषण टूल के साथ आपके सिस्टम में बाधाओं को माप नहीं लिया है।

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

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

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

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

और स्पष्ट बताते हुए जोखिम के जोखिम पर, आप नहीं चाहते हैं कि आपकी कतार समय से गुजरती है। चाबियाँ समय शुरू होनी चाहिए। कतारों में प्रत्येक रिकॉर्ड के लिए, समय बीतने के लिए लगातार अद्यतन किया जाना चाहिए, लेकिन प्रत्येक रिकॉर्ड का प्रारंभ समय नहीं बदलता है।

+0

हां, आप सही हैं कि इसे प्रति-संदेश परिप्रेक्ष्य से बेहतर समझाया जा सकता है। मेरी गलती! हालांकि, मुझे नहीं लगता कि यह एक समयपूर्व अनुकूलन है क्योंकि मुझे पहले से ही पारंपरिक ढेर के साथ अनुभव है - जब संदेश थ्रूपुट बहुत अधिक होता है तो यह वास्तव में बहुत अधिक कर देता है। – trustin

+0

वैसे भी, मैं आपके समाधान को डेविड के साथ भी जोड़ता हूं लेकिन यह बहुत बुरा है कि मैं दो उत्तरों का चयन नहीं कर सकता। आपकी अंतर्दृष्टि के लिए धन्यवाद! – trustin

+0

मैं भी लिंक करता हूं ... -> मुझे यह भी लगता है ... – trustin

2

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

6

उपयोग Hashed Timing Wheel - अधिक जानकारी के लिए गूगल 'टुकड़ों में बांटा श्रेणीबद्ध समय पहियों'। यह यहां लोगों द्वारा किए गए उत्तरों का एक सामान्यीकरण है। मैं एक बड़े व्हील आकार के साथ पदानुक्रमित समय पहियों के लिए एक हैशड टाइमिंग व्हील पसंद करूंगा।

11

मेरे सबसे अच्छे ज्ञान के लिए (मैंने एक नई प्राथमिकता कतार के बारे में एक पेपर लिखा, जिसने पिछले परिणामों की भी समीक्षा की), कोई प्राथमिकता कतार कार्यान्वयन फाइबोनैकी ढेर की सीमाओं के साथ-साथ स्थिर-समय वृद्धि-कुंजी भी प्राप्त नहीं करता है।

हो रही है कि सचमुच के साथ एक छोटी सी समस्या है। आप को बढ़ाने के महत्वपूर्ण हे में मिल सकता है, तो (1), तो आप हे में हटाने के मिल सकता है (1) - बस + अनंत के लिए महत्वपूर्ण वृद्धि (आप कतार में कुछ मानक परिशोधन चाल का उपयोग कर + infinitys के बहुत से भरा जा रहा है संभाल कर सकते हैं)। लेकिन अगर खोज-मिनट भी ओ (1) है, तो इसका मतलब है कि हटाएं-min = find-min + delete ओ (1) बन जाता है। क्योंकि छँटाई बाध्य संकेत मिलता है कि एक तुलना के आधार पर प्राथमिकता कतार में असंभव है (सब कुछ सम्मिलित है, तो एक-एक करके निकालने के लिए) है कि

n * डालने + n * हटाना मिनट > n लॉग एन।

  • नहीं तुलना हो:

यहां मुद्दा यह है कि अगर आप हे में वृद्धि महत्वपूर्ण समर्थन करने के लिए एक प्राथमिकता-पंक्ति चाहते हैं (1), तो आप निम्नलिखित दंड में से एक को स्वीकार करना चाहिए है आधारित। असल में, यह चीजों को पाने के लिए एक बहुत अच्छा तरीका है, उदा। vEB trees

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

    +3

    क्या आपके पास अपने पेपर का लिंक है? – NightWolf

    3

    ओ (1) में सभी आवेषण और हटाने के लिए एक बहुत ही आसान तरीका है, इस तथ्य का लाभ उठाते हुए कि 1) प्राथमिकता समय पर आधारित है और 2) आपके पास शायद समय-समय पर एक छोटी, निश्चित संख्या अवधि है।

    1. 10 सेकंड में टाइमआउट के सभी कार्यों को रखने के लिए एक नियमित फीफो कतार बनाएं।चूंकि सभी कार्यों में समान टाइमआउट अवधि होती है, इसलिए आप केवल अंत में सम्मिलित कर सकते हैं और कतार को क्रमबद्ध रखने के लिए शुरुआत से हटा सकते हैं।
    2. 30-सेकंड टाइमआउट अवधि वाले कार्यों के लिए एक और फीफो कतार बनाएं। अन्य टाइमआउट अवधि के लिए और कतार बनाएं।
    3. रद्द करने के लिए, कतार से आइटम को हटा दें। यह ओ (1) है यदि कतार एक लिंक्ड सूची के रूप में लागू किया गया है।
    4. Rescheduling रद्द-सम्मिलन के रूप में किया जा सकता है, क्योंकि दोनों ऑपरेशन ओ (1) हैं। ध्यान दें कि कार्यों को अलग-अलग कतारों में पुन: निर्धारित किया जा सकता है।
    5. अंत में, सभी फीफो कतारों को एक समग्र समग्र प्राथमिकता कतार में संयोजित करने के लिए, प्रत्येक फीफो कतार का सिर नियमित ढेर में भाग लेता है। इस ढेर का मुखिया सभी कार्यों में से जल्द ही समाप्त होने वाली टाइमआउट के साथ कार्य होगा।

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

    +1

    इस संबंध में: "Rescheduling को रद्द करने के रूप में किया जा सकता है, क्योंकि दोनों ऑपरेशन ओ (1) हैं।" या तो मुझे समझ में नहीं आता कि आपका क्या मतलब है या आप गलत हैं। यदि मैं एक लिंक को एक लिंक के रूप में लागू कतार के बीच में एक कुंजी को फिर से निर्धारित करना चाहता हूं, तो स्पष्ट रूप से पुन: निर्धारित करना ओ (1) नहीं है क्योंकि कुंजी ढूंढना पहले से ही ओ (एन) है। – erszcz

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