2009-10-15 21 views
5

मैंने कोड किया है .... मुझे विश्वास है कि मेरा दृष्टिकोण सही है, लेकिन मैं 100% निश्चित नहीं हूं। धागे के संबंध में, मुझे समझ में नहीं आता कि मैं ExecutorService का उपयोग करने के बजाय (new MatrixThread(...)).start() क्यों नहीं चला सकता।गुणा

इसके अतिरिक्त

, जब मैं बेंचमार्क ... दृष्टिकोण बनाम शास्त्रीय दृष्टिकोण, शास्त्रीय ज्यादा तेजी से होता है ...

क्या मैं गलत कर रहा हूँ?

पीएस यदि कोई और स्पष्टीकरण की आवश्यकता है तो कृपया मुझे बताएं।

+0

आपके कोड में "गुणा करें" विधि –

+1

गुम है क्यों आप इस तरह कुछ मल्टीथ्रेड करेंगे? यह पूरी तरह से सीपीयू-बाध्य है, ऐसा नहीं है कि आपके पास थ्रेड अवरुद्ध है I/O के लिए प्रतीक्षा कर रहा है। –

+0

मल्टीथ्रेडिंग ठीक काम कर सकती है, लेकिन यह कितनी सीपीयू पर निर्भर करती है (10x10 10x10 द्वारा गुणा करें आपके उदाहरण में 100 धागे बनाता है ... आपके पास केवल 2-8 सीपीयू है) और मैट्रिस कितने बड़े हैं (क्या वे फिट हैं एल 2/एल 3 कैश?)। एमकेएल और ओपनसीएल जैसे मूल पुस्तकालय इस का एक बेहतर काम करते हैं। – basszero

उत्तर

5

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

execute पर धागा भेजने के लिए भी अनावश्यक है; इसकी सभी जरूरतों को Runnable है। आप इन परिवर्तनों को लागू करके एक बड़ा प्रदर्शन को बढ़ावा देने की जाएगी:

  1. ExecutorService एक स्थिर सदस्य बनाने, यह आकार वर्तमान प्रोसेसर के लिए, और यह एक ThreadFactory तो यह main के बाद चल रहे प्रोग्राम नहीं रखता भेज पूरा कर दिया है। (यह शायद एक स्थिर क्षेत्र के रूप में यह ध्यान में रखते हुए बजाय विधि के लिए एक पैरामीटर के रूप में यह भेजने के लिए वास्तुकला क्लीनर होगा, मैं छोड़ कि पाठक के लिए एक व्यायाम ☺ के रूप में।)

    private static final ExecutorService workerPool = 
        Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new ThreadFactory() { 
         public Thread newThread(Runnable r) { 
          Thread t = new Thread(r); 
          t.setDaemon(true); 
          return t; 
         } 
        }); 
    
  2. MatrixThreadRunnable लागू नहीं बल्कि बनाओ Thread के उत्तराधिकारी से। धागे बनाने के लिए महंगी हैं; पीओजेओ बहुत सस्ते हैं। आप इसे static भी बना सकते हैं जो उदाहरणों को छोटा बनाता है (क्योंकि गैर स्थैतिक वर्ग संलग्न वस्तु के लिए एक निहित संदर्भ प्राप्त करते हैं)।

    private static class MatrixThread implements Runnable 
    
  3. परिवर्तन से

    (1), आप अब awaitTermination कोई यकीन है कि सभी कार्य समाप्त कर रहे हैं (इस कार्यकर्ता पूल के रूप में) बनाने के लिए कर सकते हैं।इसके बजाय, submit विधि का उपयोग करें जो Future<?> देता है। किसी भी सूची में सभी भावी वस्तुओं को एकत्रित करें, और जब आप सभी कार्यों को सबमिट करते हैं, तो सूची में पुनरावृत्त करें और प्रत्येक ऑब्जेक्ट के लिए get पर कॉल करें।

    public Matrix multiply(Matrix multiplier) throws InterruptedException { 
        Matrix result = new Matrix(dimension); 
        List<Future<?>> futures = new ArrayList<Future<?>>(); 
        for(int currRow = 0; currRow < multiplier.dimension; currRow++) { 
         for(int currCol = 0; currCol < multiplier.dimension; currCol++) {    
          Runnable worker = new MatrixThread(this, multiplier, currRow, currCol, result); 
          futures.add(workerPool.submit(worker)); 
         } 
        } 
        for (Future<?> f : futures) { 
         try { 
          f.get(); 
         } catch (ExecutionException e){ 
          throw new RuntimeException(e); // shouldn't happen, but might do 
         } 
        } 
        return result; 
    } 
    

    यह एकल पिरोया संस्करण की तुलना में तेजी हो जाएगा:

आपका multiply विधि अब कुछ इस तरह देखना चाहिए? खैर, मेरे तर्कसंगत क्रैपी बॉक्स पर मल्टीथ्रेड संस्करण n < 1024.

के मूल्यों के लिए धीमा है, हालांकि यह सतह को खरोंच कर रहा है। अपनी स्मृति की खपत O(n²) है, जो एक बहुत बुरा संकेत है - असली समस्या यह है कि आप एक बहुत MatrixThread की उदाहरण बना है। MatrixThread.run में लूप को आंतरिक स्थानांतरित करने से craploads (आदर्श रूप से, आप कार्यकर्ता धागे की तुलना में अधिक कार्य नहीं बनाते) के प्रदर्शन में सुधार करेंगे।


संपादित करें: मैं अधिक दबाव काम करने के लिए है के रूप में, मैं विरोध इस आगे के अनुकूलन नहीं कर सका। मैं इस के साथ आया था (... कोड की horrendously बदसूरत टुकड़ा) है कि "केवल" O(n) नौकरियों बनाता है:

public Matrix multiply(Matrix multiplier) throws InterruptedException { 
    Matrix result = new Matrix(dimension); 
    List<Future<?>> futures = new ArrayList<Future<?>>(); 
    for(int currRow = 0; currRow < multiplier.dimension; currRow++) { 
     Runnable worker = new MatrixThread2(this, multiplier, currRow, result); 
     futures.add(workerPool.submit(worker)); 
    } 
    for (Future<?> f : futures) { 
     try { 
      f.get(); 
     } catch (ExecutionException e){ 
      throw new RuntimeException(e); // shouldn't happen, but might do 
     } 
    } 
    return result; 
} 


private static class MatrixThread2 implements Runnable 
{ 
    private Matrix self, mul, result; 
    private int row, col;  

    private MatrixThread2(Matrix a, Matrix b, int row, Matrix result) 
    {   
     this.self = a; 
     this.mul = b; 
     this.row = row; 
     this.result = result; 
    } 

    @Override 
    public void run() 
    { 
     for(int col = 0; col < mul.dimension; col++) { 
     int cellResult = 0; 
     for (int i = 0; i < self.getMatrixDimension(); i++) 
      cellResult += self.template[row][i] * mul.template[i][col]; 
     result.template[row][col] = cellResult; 
     } 
    } 
} 

यह अभी भी महान नहीं है, लेकिन मूल रूप से मल्टी-थ्रेडेड संस्करण कुछ भी गणना कर सकता है आप रोगी हो जाएगा प्रतीक्षा करने के लिए पर्याप्त है, और यह एकल-थ्रेडेड संस्करण से तेज़ कर देगा।

+0

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

+0

ठीक है, कई हिस्सों में नौकरी को विभाजित करने में हमेशा एक ओवरहेड होता है। 'N' के छोटे मानों के लिए बहु-थ्रेडेड संस्करण हमेशा धीमा हो जाएगा, लेकिन बड़ा' n' हो जाता है, बेहतर बहु-थ्रेडेड संस्करण होने की संभावना है। इस समाधान में अभी भी बहुत अधिक ओवरहेड है क्योंकि यह 'n' कार्यों को बनाता है (इस प्रकार 'ओ (एन)' के सिंक्रनाइज़ेशन ओवरहेड होता है)। यदि आप गुणा को विभाजित करने के लिए कुछ निश्चित संख्या में कार्यों को विभाजित कर सकते हैं (कहें, 'उपलब्ध प्रोसेसर * 2' या कुछ) प्रोग्राम' एन' के बड़े मूल्यों के लिए तेज़ हो जाएगा। – gustafc

+0

इसके अलावा, 'एन' के छोटे मूल्यों के लिए आप हमेशा गैर-थ्रेडेड गुणा कर सकते हैं क्योंकि हमेशा तेज होने की संभावना है। – gustafc

6

निष्पादक सेवा का उपयोग करते समय भी धागे बनाने में शामिल ओवरहेड का एक गुच्छा है। मुझे संदेह है कि आप बहुप्रचारित दृष्टिकोण क्यों हैं, इतनी धीमी है कि आप 99% एक नया धागा बना रहे हैं और केवल 1% या उससे कम, वास्तविक गणित कर रहे हैं।

आमतौर पर, इस समस्या को हल करने के लिए आप एक साथ पूरे ऑपरेशन का बैच लेंगे और उन्हें एक थ्रेड पर चलाएंगे। मैं 100% नहीं हूं कि इस मामले में ऐसा कैसे करें, लेकिन मैं सुझाव देता हूं कि आपके मैट्रिक्स को छोटे हिस्सों में कहें (कहें, 10 छोटी मैट्रिस) और थ्रेड पर चलें, प्रत्येक सेल को अपने थ्रेड में चलाने के बजाए।

1

सबसे पहले, आपको आकार के एक नए फिक्स्ड थ्रेडपूल का उपयोग करना चाहिए, जो आपके पास है, आपके द्वारा उपयोग किए जाने वाले क्वाडकोर पर 4 कोर हैं। दूसरा, प्रत्येक मैट्रिक्स के लिए नया न बनाएं।

आप executorservice एक स्थिर सदस्य चर मैं 512

इसके अलावा के एक मैट्रिक्स आकार में पिरोया संस्करण के लगभग लगातार तेजी से निष्पादन प्राप्त करते हैं, तो MatrixThread विस्तार थ्रेड के बजाय Runnable लागू करने के लिए बदल भी करने के लिए निष्पादन को गति जहां थ्रेड किया गया है मेरी मशीन 2x पर 512

+0

बहुत बहुत धन्यवाद, मैं इसे ध्यान में रखूंगा! –

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