2009-10-30 12 views
15

मैं यह पता लगाने की कोशिश कर रहा हूं कि जावा के एक्जिक्यूटर्स का सही तरीके से उपयोग कैसे करें। मुझे लगता है कि ExecutorService पर कार्यों को सबमिट करने का एहसास है। हालांकि, मुझे आश्चर्य है कि यह उतना ऊंचा है जितना यह है।निष्पादक सेवा की आश्चर्यजनक प्रदर्शन ब्रेक-इवेंट पॉइंट --- अंगूठे के नियम?

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

मैं बहुत सरल कुछ परीक्षण करने के लिए करने की कोशिश की: "Math.random() * Math.random()"

सबसे सरल परीक्षण एक सरल पाश में इस गणना चलाता है। दूसरा परीक्षण अज्ञात रननेबल के अंदर समान गणना करता है (यह नई वस्तुओं को बनाने की लागत को मापने के लिए माना जाता है)। तीसरा परीक्षण Runnable को ExecutorService पर पास करता है (यह निष्पादकों को पेश करने की लागत को मापता है)।

(in milliseconds) 
simpleCompuation:47 
computationWithObjCreation:62 
computationWithObjCreationAndExecutors:422 

(के बारे में एक बार चार रन से बाहर है, पहले दो नंबर ऊपर बराबर होने के अंत)

नोटिस:

मैं अपने Dinky लैपटॉप पर परीक्षण (2 सीपीयू, 1.5 गिग राम) भाग गया कि निष्पादक एक धागे पर निष्पादन से कहीं अधिक दूर लेते हैं। संख्या 1 और 8 के बीच थ्रेड पूल आकार के लिए समान थी।

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


import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
import java.util.concurrent.TimeUnit; 

public class ExecServicePerformance { 

private static int count = 100000; 

public static void main(String[] args) throws InterruptedException { 

    //warmup 
    simpleCompuation(); 
    computationWithObjCreation(); 
    computationWithObjCreationAndExecutors(); 

    long start = System.currentTimeMillis(); 
    simpleCompuation(); 
    long stop = System.currentTimeMillis(); 
    System.out.println("simpleCompuation:"+(stop-start)); 

    start = System.currentTimeMillis(); 
    computationWithObjCreation(); 
    stop = System.currentTimeMillis(); 
    System.out.println("computationWithObjCreation:"+(stop-start)); 

    start = System.currentTimeMillis(); 
    computationWithObjCreationAndExecutors(); 
    stop = System.currentTimeMillis(); 
    System.out.println("computationWithObjCreationAndExecutors:"+(stop-start)); 


} 

private static void computationWithObjCreation() { 
    for(int i=0;i<count;i++){ 
    new Runnable(){ 

    @Override 
    public void run() { 
    double x = Math.random()*Math.random(); 
    } 

    }.run(); 
    } 

} 

private static void simpleCompuation() { 
    for(int i=0;i<count;i++){ 
    double x = Math.random()*Math.random(); 
    } 

} 

private static void computationWithObjCreationAndExecutors() 
    throws InterruptedException { 

    ExecutorService es = Executors.newFixedThreadPool(1); 
    for(int i=0;i<count;i++){ 
    es.submit(new Runnable() { 
    @Override 
    public void run() { 
    double x = Math.random()*Math.random();  
    } 
    }); 
    } 
    es.shutdown(); 
    es.awaitTermination(10, TimeUnit.SECONDS); 
} 
} 
+0

वाह, पूर्वावलोकन ने अंतिम परिणाम की तुलना में कोड को कहीं बेहतर स्वरूपित किया। मैं इसे कैसे ठीक करूं? – Shahbaz

+1

मैंने अभी इसे दोबारा सुधार दिया, बेहतर दिख रहा है? –

+0

धन्यवाद जेडजेड कोडर, कोड अब ऐसा लगता है कि – Shahbaz

उत्तर

19
  1. का उपयोग executors, उपयोग सीपीयू और/या सीपीयू कोर के बारे में है।
  2. आप सही हैं, नई वस्तुओं की लागत बहुत अधिक है। तो खर्च को कम करने का एक तरीका बैचों का उपयोग करना है। यदि आप समझने के लिए गणना और मात्रा की मात्रा जानते हैं, तो आप बैच बनाते हैं। तो एक निष्पादित कार्य में किए गए हजारों (एस) गणनाओं के बारे में सोचें। आप प्रत्येक धागे के लिए बैच बनाते हैं। जैसे ही गणना की जाती है (java.util.concurrent.Future), आप अगला बैच बनाते हैं। यहां तक ​​कि नए बैचों का निर्माण पैरारल में भी किया जा सकता है (4 सीपीयू -> गणना के लिए 3 धागे, बैच प्रावधान के लिए 1 धागा)। अंत में, आप अधिक थ्रूपुट के साथ समाप्त हो सकते हैं, लेकिन उच्च स्मृति मांगों (बैचों, प्रावधान) के साथ।

संपादित करें: मैंने आपका उदाहरण बदल दिया और मैंने इसे अपने छोटे दोहरे कोर x200 लैपटॉप पर चलाने दिया।

provisioned 2 batches to be executed 
simpleCompuation:14 
computationWithObjCreation:17 
computationWithObjCreationAndExecutors:9 

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

खुद के द्वारा परिणाम देखें ...

import java.util.List; 
import java.util.Vector; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
import java.util.concurrent.TimeUnit; 

public class ExecServicePerformance { 

    private static int count = 100000; 

    public static void main(String[] args) throws InterruptedException { 

     final int cpus = Runtime.getRuntime().availableProcessors(); 

     final ExecutorService es = Executors.newFixedThreadPool(cpus); 

     final Vector<Batch> batches = new Vector<Batch>(cpus); 

     final int batchComputations = count/cpus; 

     for (int i = 0; i < cpus; i++) { 
      batches.add(new Batch(batchComputations)); 
     } 

     System.out.println("provisioned " + cpus + " batches to be executed"); 

     // warmup 
     simpleCompuation(); 
     computationWithObjCreation(); 
     computationWithObjCreationAndExecutors(es, batches); 

     long start = System.currentTimeMillis(); 
     simpleCompuation(); 
     long stop = System.currentTimeMillis(); 
     System.out.println("simpleCompuation:" + (stop - start)); 

     start = System.currentTimeMillis(); 
     computationWithObjCreation(); 
     stop = System.currentTimeMillis(); 
     System.out.println("computationWithObjCreation:" + (stop - start)); 

     // Executor 

     start = System.currentTimeMillis(); 
     computationWithObjCreationAndExecutors(es, batches);  
     es.shutdown(); 
     es.awaitTermination(10, TimeUnit.SECONDS); 
     // Note: Executor#shutdown() and Executor#awaitTermination() requires 
     // some extra time. But the result should still be clear. 
     stop = System.currentTimeMillis(); 
     System.out.println("computationWithObjCreationAndExecutors:" 
       + (stop - start)); 
    } 

    private static void computationWithObjCreation() { 

     for (int i = 0; i < count; i++) { 
      new Runnable() { 

       @Override 
       public void run() { 

        double x = Math.random() * Math.random(); 
       } 

      }.run(); 
     } 

    } 

    private static void simpleCompuation() { 

     for (int i = 0; i < count; i++) { 
      double x = Math.random() * Math.random(); 
     } 

    } 

    private static void computationWithObjCreationAndExecutors(
      ExecutorService es, List<Batch> batches) 
      throws InterruptedException { 

     for (Batch batch : batches) { 
      es.submit(batch); 
     } 

    } 

    private static class Batch implements Runnable { 

     private final int computations; 

     public Batch(final int computations) { 

      this.computations = computations; 
     } 

     @Override 
     public void run() { 

      int countdown = computations; 
      while (countdown-- > -1) { 
       double x = Math.random() * Math.random(); 
      } 
     } 
    } 
} 
+0

दिलचस्प समाधान। मुझे अधिकारियों के उपयोग को बदलने के तरीके के बारे में कुछ विचार देता है। – Shahbaz

+0

+1, बहुत अच्छा उदाहरण। –

+0

हाय, अगर मैं एक MacOSX डुअल कोर पर इस उदाहरण चलाने के लिए, मुझे मिल गया: simpleComputation: 268 computationWithObjCreation: 155 computation2: 0, क्योंकि computationWithObjCreationAndExecutors का परिणाम प्राप्त नहीं होता? अगर मैं es.shutdown() और es.awaitTermination चले गए इससे पहले कि हम बंद होने का समय है, तो परिणाम ले: प्रावधान: 2 बैचों निष्पादित किया जाना है simpleComputation: 261 computationWithObjCreation: 92 computationWithObjCreationAndExecutors: 126 जहां computationWithObjCreationAndExecutors लगातार गणना के साथ खराब प्रदर्शन करता है WithObjCreation। ऐसा क्यों हो रहा है? – portoalet

6

ऐसा निम्न कारणों से थ्रेड पूल के लिए एक उचित परीक्षण नहीं है,

  1. आप सब पर पूलिंग का लाभ नहीं ले रहे हैं क्योंकि आप केवल 1 धागा है।
  2. नौकरी बहुत आसान है कि पूलिंग ओवरहेड को उचित नहीं ठहराया जा सकता है। एफपीपी के साथ एक सीपीयू पर एक गुणा केवल कुछ चक्र लेता है।

अतिरिक्त कदम थ्रेड पूल ऑब्जेक्ट निर्माण के अलावा करना है निम्नलिखित को ध्यान में रखते और काम चल रहा है,

  1. कतार
  2. में काम रखो कतार से काम निकालें
  3. जाओ पूल से धागा और नौकरी निष्पादित करें
  4. पूल में धागे को वापस लौटें

जब आपके पास असली नौकरी और एकाधिक धागे हैं, तो थ्रेड पूल का लाभ स्पष्ट होगा।

+1

मैं दूसरा जेडजेड कोडर; मेरे अनुभव में जब आपका धागा पूल बड़ा होता है तो लाभ अधिक स्पष्ट हो जाएंगे। – Everyone

+0

निष्पादक को थ्रेड प्राप्त करने और "वापसी" करने की आवश्यकता नहीं है। यह एक आंतरिक कार्यकर्ता धागा बनाता है जो चुनाव() कार्यों की कतार है। इसके अलावा, कार्य की कम समय जटिलता को देखते हुए, यह केवल एक ही धागे का उपयोग करने के शायद एक फायदा है अन्यथा, वहाँ BlockingQueue में ताला तर्क दिया जा रहा है और अंदर और बाहर कार्यकर्ता धागे चलती के साथ मुद्दों के कारण का एक मौका है चलने योग्य राज्य वास्तविक लागत? धागे को समाप्त करने के लिए इंतजार करते समय एक धागा बनाने के लिए कर्नेल पर जाकर एक ब्लॉकिंग ऑपरेशन को कॉल करना। 100,000 बहुत कुछ नहीं है। लेकिन सबक सीखा, प्रदर्शन ट्यूनिंग परीक्षण की आवश्यकता है। –

+0

मैंने 1 और 8 के बीच थ्रेड पूल आकारों का प्रयास किया, वे सभी एक ही संख्या के बारे में लौट आए।मैं 1 के पूल आकार पर केंद्रित था क्योंकि मैं निष्पादक ढांचे के ऊपरी हिस्से को मापना चाहता था। आपकी टिप्पणी मजबूत करती है कि मुझे ढांचे के आंतरिक भाग का अध्ययन करने की आवश्यकता है। – Shahbaz

0

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

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

निश्चित रूप से एक छोटे से ऑपरेशन के लिए एक और धागे पर जा रहा है तेजी से नहीं जा रहा है। यदि संभव हो तो बड़े आकार में कार्यों को विभाजित करें। जेडीके 7 ऐसा लगता है कि इसमें फोर्क-जॉइन फ्रेमवर्क होगा, जो निष्क्रिय कार्यों को खींचने के लिए बड़े कार्यों को निष्क्रिय थ्रेड द्वारा निकाले गए कार्यों के साथ क्रमशः उसी थ्रेड पर कार्यों को निष्पादित करने से पहले एल्गोरिदम को विभाजित करने और जीतने का प्रयास करता है। इसलिए यदि आप एक थ्रेड पूल है कि सबसे अच्छे रूप में CPU की राशि का इस्तेमाल करता है, तो आप सीपीयू/कोर के रूप में कई धागे करना होगा बनाने

+0

कई बार परीक्षण चलाने के बारे में अच्छी बात है। मैंने वास्तव में इसे कई बार चलाया, मैंने अभी एक ही परिणाम चिपकाया। मुझे बेंचमार्क में सुधार के बारे में आपका मुद्दा मिलता है। – Shahbaz

4

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

यदि आप फिर से बेंचमार्किंग का प्रयास करते हैं लेकिन एक बार फ़ील्ड के रूप में सेवा शुरू करते हैं, एक बार, टाइम लूप के बाहर; तो आप सेवा बनाम रननेबल्स सबमिट करने के वास्तविक ओवरहेड को स्वयं बनाते हुए देखेंगे।

लेकिन मुझे नहीं लगता कि आप बिंदु पूरी तरह से समझा दिया है - निष्पादकों दक्षता के लिए वहाँ हो नहीं होती हैं, वे वहाँ हो एक धागा पूल सरल करने के लिए समन्वय और सौंपने बंद काम करने के लिए। वे हमेशा Runnable.run() खुद को आमंत्रित करने से कम कुशल होंगे (क्योंकि दिन के अंत में निष्पादक सेवा को अभी भी कुछ अतिरिक्त हाउसकीपिंग करने के बाद, ऐसा करने की आवश्यकता है)। यह तब होता है जब आप उन्हें अतुल्यकालिक प्रक्रिया की आवश्यकता वाले कई धागे से उपयोग कर रहे हैं, कि वे वास्तव में चमकते हैं।

यह भी मान लें कि आप मूल रूप से निश्चित लागत के सापेक्ष समय अंतर को देख रहे हैं (निष्पादक ओवरहेड वही है जो आपके कार्यों को 1ms या 1hr चलाने के लिए लेता है) एक बहुत ही कम परिवर्तनीय राशि (आपके छोटे चलने योग्य) की तुलना में।यदि एक्जिकलर सेवा को 1 एमएमएस कार्य चलाने के लिए 5 एमएमएस अतिरिक्त लगता है, तो यह एक बहुत अनुकूल आंकड़ा नहीं है। यदि 5 सेकंड कार्य (जैसे एक गैर-तुच्छ एसक्यूएल क्वेरी) चलाने के लिए 5 एमएमएस अतिरिक्त लगता है, तो यह पूरी तरह से नगण्य है और पूरी तरह से इसके लायक है।

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

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

+0

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

0

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

2

Math.random() वास्तव में एक रैंडम नंबर जेनरेटर पर सिंक्रनाइज़ करता है। कॉलिंग Math.random() परिणाम जनरेटर के लिए महत्वपूर्ण विवाद में परिणाम। वास्तव में आपके पास जितने अधिक धागे हैं, उतना ही धीमा हो जाएगा।

math.random() जावाडोक से:

इस विधि ठीक से एक से अधिक एक थ्रेड द्वारा सही उपयोग की अनुमति के लिए सिंक्रनाइज़ किया जाता है। हालांकि, अगर कई धागे को छद्म यादृच्छिक संख्याओं को एक महान दर पर उत्पन्न करने की आवश्यकता है, तो यह प्रत्येक थ्रेड के लिए पर विवाद को कम कर सकता है, जिसका अपना छद्म यादृच्छिक-संख्या जनरेटर है।

0

प्रत्येक थ्रेड में गणना के बड़े हिस्से सबमिट करने के लिए आपको समूह निष्पादन की आवश्यकता है (उदाहरण के लिए स्टॉक प्रतीक के आधार पर समूह बनाएं)। मुझे विघटनकर्ता का उपयोग करके समान परिदृश्यों में सर्वोत्तम परिणाम मिला। यह बहुत कम प्रति नौकरी ओवरहेड है। समूह नौकरियों के लिए अभी भी महत्वपूर्ण है, बेवकूफ राउंड रॉबिन आमतौर पर कई कैश मिस बनाता है।

देख http://java-is-the-new-c.blogspot.de/2014/01/comparision-of-different-concurrency.html

2

'भूमि के ऊपर' आप का उल्लेख ExecutorService कोई लेना देना नहीं है, यह math.random पर सिंक्रनाइज़ किया जा रहा से अधिक थ्रेड, ताला विवाद बनाने के कारण होता है।

तो हाँ, आप कुछ खो रहे हैं (और नीचे 'सही' उत्तर वास्तव में सही नहीं है)।25k आपरेशन के 120 परीक्षण के लिए

import java.util.ArrayList; 
import java.util.List; 
import java.util.concurrent.CountDownLatch; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
import java.util.concurrent.TimeUnit; 
import java.util.function.DoubleFunction; 

import com.google.common.base.Stopwatch; 

public class ExecServicePerformance { 

    private static final int repetitions = 120; 
    private static int totalOperations = 250000; 
    private static final int cpus = 8; 
    private static final List<Batch> batches = batches(cpus); 

    private static DoubleFunction<Double> performanceFunc = (double i) -> {return Math.sin(i * 100000/Math.PI); }; 

    public static void main(String[] args) throws InterruptedException { 

     printExecutionTime("Synchronous", ExecServicePerformance::synchronous); 
     printExecutionTime("Synchronous batches", ExecServicePerformance::synchronousBatches); 
     printExecutionTime("Thread per batch", ExecServicePerformance::asynchronousBatches); 
     printExecutionTime("Executor pool", ExecServicePerformance::executorPool); 

    } 

    private static void printExecutionTime(String msg, Runnable f) throws InterruptedException { 
     long time = 0; 
     for (int i = 0; i < repetitions; i++) { 
      Stopwatch stopwatch = Stopwatch.createStarted(); 
      f.run(); //remember, this is a single-threaded synchronous execution since there is no explicit new thread 
      time += stopwatch.elapsed(TimeUnit.MILLISECONDS); 
     } 
     System.out.println(msg + " exec time: " + time); 
    }  

    private static void synchronous() { 
     for (int i = 0; i < totalOperations; i++) { 
      performanceFunc.apply(i); 
     } 
    } 

    private static void synchronousBatches() {  
     for (Batch batch : batches) { 
      batch.synchronously(); 
     } 
    } 

    private static void asynchronousBatches() { 

     CountDownLatch cb = new CountDownLatch(cpus); 

     for (Batch batch : batches) { 
      Runnable r =() -> { batch.synchronously(); cb.countDown(); }; 
      Thread t = new Thread(r); 
      t.start(); 
     } 

     try { 
      cb.await(); 
     } catch (InterruptedException e) { 
      throw new RuntimeException(e); 
     }   
    } 

    private static void executorPool() { 

     final ExecutorService es = Executors.newFixedThreadPool(cpus); 

     for (Batch batch : batches) { 
      Runnable r =() -> { batch.synchronously(); }; 
      es.submit(r); 
     } 

     es.shutdown(); 

     try { 
      es.awaitTermination(10, TimeUnit.SECONDS); 
     } catch (InterruptedException e) { 
      throw new RuntimeException(e); 
     } 

    } 

    private static List<Batch> batches(final int cpus) { 
     List<Batch> list = new ArrayList<Batch>(); 
     for (int i = 0; i < cpus; i++) { 
      list.add(new Batch(totalOperations/cpus)); 
     } 
     System.out.println("Batches: " + list.size()); 
     return list; 
    } 

    private static class Batch { 

     private final int operationsInBatch; 

     public Batch(final int ops) { 
      this.operationsInBatch = ops; 
     } 

     public void synchronously() { 
      for (int i = 0; i < operationsInBatch; i++) { 
       performanceFunc.apply(i); 
      } 
     } 
    } 


} 

परिणाम समय (ms)::

    यहाँ कुछ जावा 8 एक सरल समारोह जिसमें कोई ताला विवाद नहीं है चल रहा है 8 धागे प्रदर्शित करने के लिए कोड है

  • तुल्यकालिक कार्यकारी समय: 9956
  • तुल्यकालिक बैचों कार्यकारी समय: 9900
  • प्रति बैच थ्रेड कार्यकारी समय: 2176
  • निष्पादक पूल निष्पादन समय: 1 9 22

विजेता: निष्पादक सेवा।

1

यहाँ मेरी मशीन (OpenJDK 8 64-बिट पर उबंटू 14.0, Thinkpad W530) पर परिणाम हैं

simpleCompuation:6 
computationWithObjCreation:5 
computationWithObjCreationAndExecutors:33 

वहाँ निश्चित रूप से भूमि के ऊपर है। लेकिन याद रखें कि ये संख्याएं क्या हैं: मिलीसेकंड 100k पुनरावृत्तियों के लिए मिलीसेकंड। आपके मामले में, उपरिवर्तन प्रति पुनरावृत्ति के बारे में 4 माइक्रोसॉन्ड था। मेरे लिए, ओवरहेड लगभग एक चौथाई माइक्रोसॉन्ड था।

भूमि के ऊपर (निश्चित रूप से और अधिक जटिल अपने पाश के लिए की तुलना में) जटिल कोड रास्तों की वजह से तुल्यकालन, आंतरिक डाटा संरचनाओं, और संभवतः JIT अनुकूलन की कमी है।

कार्यों कि आप वास्तव में parallelize करना चाहते हैं इसके लायक हो सकता है, तिमाही माइक्रोसेकंड भूमि के ऊपर के बावजूद।


एफवाईआई, यह समांतरता के लिए एक बहुत ही खराब गणना होगी। मैंने धागे को 8 (कोर की संख्या) तक बढ़ा दिया:

simpleCompuation:5 
computationWithObjCreation:6 
computationWithObjCreationAndExecutors:38 

इससे कोई तेज़ नहीं हुआ। ऐसा इसलिए है क्योंकि Math.random() सिंक्रनाइज़ किया गया है।

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