2013-05-20 2 views
16

हमारा विरासत ऐप एक भयानक ढांचे के साथ फंस गया है (ठीक है, मैं नामों का नाम दूंगा, यह टेपेस्ट्री 4 है) जिसमें सबसे आसान के लिए EventListeners (~ 100,000) की हास्यास्पद संख्या शामिल है संचालन। मुझे लगता है कि javax.swing.event.EventListenerList को संभालने के लिए यह कभी भी था, और इस दुर्भाग्यपूर्ण उपयोग के मामले में यह हमें कुछ खराब प्रदर्शन सिरदर्द पैदा कर रहा है।EventListenerList क्यों नहीं बदला गया है? (या: इसे बदलने में समस्याएं क्या हैं?)

मैं कुछ घंटों बिताए नीचे काफी अनुभवहीन HashMap/ArrayList आधारित प्रतिस्थापन अप सजा, और यह बड़े पैमाने पर तेजी से लगभग हर तरह से है:

  • EventListenerList> 2 सेकंड
  • :

    50,000 श्रोताओं जोड़े

  • EventListenerMap ~ 3.5 मिलीसेकंड

आग 50,000 श्रोताओं के लिए घटना:

  • EventListenerList 0.3-0.5 मिलीसेकेंड
  • EventListenerMap 0.4-0.5 मिलीसेकेंड

50.000 श्रोताओं निकालें (एक समय में एक):

  • EventListenerList> 2 सेकंड
  • EventListenerMap ~ 280 मिलीसेकंड

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

public class EventListenerMap 
{ 

    private final ReadWriteLock lock = new ReentrantReadWriteLock(); 
    private final Lock readLock = lock.readLock(); 
    private final Lock writeLock = lock.writeLock(); 

    private Map<Class, List> llMap = new HashMap<Class, List>(); 

    public <L extends EventListener> void add (Class<L> listenerClass, L listener) 
    { 
     try 
     { 
      writeLock.lock(); 
      List<L> list = getListenerList(listenerClass); 
      if (list == null) 
      { 
       list = new ArrayList<L>(); 
       llMap.put(listenerClass, list); 
      } 
      list.add(listener); 
     } 
     finally 
     { 
      writeLock.unlock(); 
     } 
    } 

    public <L extends EventListener> void remove (Class<L> listenerClass, L listener) 
    { 
     try 
     { 
      writeLock.lock(); 
      List<L> list = getListenerList(listenerClass); 
      if (list != null) 
      { 
       list.remove(listener); 
      } 
     } 
     finally 
     { 
      writeLock.unlock(); 
     } 
    } 

    @SuppressWarnings("unchecked") 
    public <L extends EventListener> L[] getListeners (Class<L> listenerClass) 
    { 
     L[] copy = (L[]) Array.newInstance(listenerClass, 0); 
     try 
     { 
      readLock.lock(); 
      List<L> list = getListenerList(listenerClass); 
      if (list != null) 
      { 
       copy = (L[]) list.toArray(copy); 
      } 
     } 
     finally 
     { 
      readLock.unlock(); 
     } 
     return copy; 
    } 

    @SuppressWarnings("unchecked") 
    private <L extends EventListener> List<L> getListenerList (Class<L> listenerClass) 
    { 
     return (List<L>) llMap.get(listenerClass); 
    } 
} 
+0

+1 पागल (iest) इस महीने के सवाल, क्यू के बारे में (सबसे सरल संचालन के लिए) चरणों के हर अलग हैं या वे पेड़ या सेमाफोर, अगर हाँ तो एक और पागल (iest) सुझाव, eventhandler का उपयोग करें और से श्रृंखलित कर रहे हैं, रनटाइम पर स्ट्रिंग फॉर्म में पथ बनाने के लिए, तो शायद स्मृति में विधियों का समूह पकड़ने की आवश्यकता नहीं है, फाहा मुझे इस विचार से प्यार है, जो आपके प्रश्न से आया है, आरपीजी हमेशा के लिए जीते हैं (फिर उसी पागल अंतहीन कोड) – mKorbel

+1

क्या आपके पास रिश्तेदार फायरिंग के समय में टाइपो? आप रिपोर्ट करते हैं कि आपका कार्यान्वयन 'EventListenerList' के लिए 0.4 _milliseconds_ बनाम 0.3 _seconds_ पर "केवल बाल धीमा" हो सकता है। –

+0

हां, वह एक टाइपो था, धन्यवाद! –

उत्तर

5

यह अनुकूलन का सवाल है। स्विंग के EventListenerList मान लिया गया है:

  • एक सूची पर श्रोताओं की संख्या बहुत छोटा है
  • ListenerLists की संख्या बहुत बड़ी
  • जोड़ें/निकालें घटनाओं बहुत निराला

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

(वास्तव में, यह संभव नहीं के रूप में काफी के रूप में स्मृति कुशल है, सूची है {प्रकार, श्रोता} जोड़े, इसलिए यदि यह सरणियों की एक सरणी था यह कुछ मामलों में थोड़ा छोटा होगा।)

के रूप में आपके समाधान के लिए: HashMap s कुशल हैशिंग सुनिश्चित करने के लिए स्मृति आवंटित करें। इसी तरह, डिफ़ॉल्ट ArrayList कन्स्ट्रक्टर 10 तत्वों के लिए स्थान आवंटित करता है और भाग में बढ़ता है। अपने विचित्र कोड-बेस में जहां प्रत्येक सूची में 100k श्रोताओं हैं, यह अतिरिक्त मेमोरी उन सभी मेमोरी के लिए एक छोटा सा अतिरिक्त है जो आप सभी श्रोताओं को पकड़ने के लिए उपयोग कर रहे हैं।

एक लाइटर श्रोता लोड के तहत, हालांकि, अपने कार्यान्वयन एक नक्शे के लिए हर खाली सूची के लिए 16 संकेत (HashMap के डिफ़ॉल्ट आवंटन), एक तत्व के साथ एक EventListenerMap के लिए 26 संकेत दिए गए, और 36 संकेत लागत विभिन्न वर्गों के दो तत्वों के साथ । (यह शेष HashMap और ArrayList संरचना आकार की गणना नहीं कर रहा है।) उन मामलों के लिए, EventListenerList क्रमशः 0, 2, और 4 पॉइंटर्स खर्च करते हैं।

ऐसा लगता है कि आपके पास प्राप्त कोड के लिए एक बड़ा सुधार है।

+0

उत्कृष्ट जवाब, धन्यवाद। –

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