2012-08-03 18 views
7

की एक धारा के लिए उच्च प्रदर्शन बफरिंग कि एक बड़ी संख्या (लाखों वर्तमान में, अंत में अरबों) अपेक्षाकृत कम की (5-100 तत्वों) यादृच्छिक संख्याओं के एरे की खपत और के साथ कुछ नहीं-बहुत-ज़ोरदार गणित करता है उन्हें। यादृच्छिक संख्याएं, अच्छी तरह से, यादृच्छिक, आदर्श रूप से मैं उन्हें एकाधिक कोर पर उत्पन्न करना चाहता हूं, क्योंकि यादृच्छिक संख्या पीढ़ी> प्रोफाइलिंग में मेरे रनटाइम का 50% है। हालांकि, मुझे बड़ी संख्या में छोटे कार्यों को वितरित करने में कठिनाई हो रही है जो एकल-थ्रेडेड दृष्टिकोण से धीमी नहीं है।मैं कोड है रैंड

for(int i=0;i<1000000;i++){ 
    for(RealVector d:data){ 
     while(!converged){ 
      double[] shortVec = new double[5]; 
      for(int i=0;i<5;i++) shortVec[i]=rng.nextGaussian(); 
      double[] longerVec = new double[50]; 
      for(int i=0;i<50;i++) longerVec[i]=rng.nextGaussian(); 
      /*Do some relatively fast math*/ 
     } 
    } 
} 

दृष्टिकोण मैं लिया है कि है नहीं काम कर रहे हैं::

मेरे कोड वर्तमान में कुछ इस तरह दिखता

  • 1+ एक ArrayBlockingQueue, पॉप्युलेट धागे और अपने मुख्य पाश लेने वाली और सरणी को पॉप्युलेट करना (मुक्केबाजी/अनबॉक्सिंग यहां हत्यारा था)
  • गणित के गैर-निर्भर हिस्सों को करते हुए एक कॉल करने योग्य (भविष्य का उत्पादन) वाले वैक्टर उत्पन्न करना (ऐसा लगता है कि जो भी समानांतरता लाभ प्राप्त हुआ है, उससे संकेत मिलता है)
  • 2 ArrayBlockingQueue का उपयोग करके, प्रत्येक थ्रेड द्वारा पॉप्युलेट किया गया है, एक छोटा और एक लंबे सरणी के लिए एक (अभी भी लगभग दो बार प्रत्यक्ष सिंगल-थ्रेडेड के रूप में धीमा मामला)।

मैं अपने विशेष समस्या कैसे समानांतर में छोटे और स्वतंत्र पुरातन की बड़ी धाराओं पैदा करने और उन्हें एक ही धागे से लेने के सामान्य मामले को संभालने के लिए के रूप में इतना करने के लिए "समाधान" के लिए नहीं देख रहा हूँ।

उत्तर

4

यह एक कतार क्योंकि का उपयोग कर से अधिक कुशल है,

  • पेलोड double[] का एक सरणी है जिसका अर्थ है कि पृष्ठभूमि थ्रेड इसे पास करने से पहले अधिक डेटा उत्पन्न कर सकता है।
  • सभी वस्तुओं को पुनर्नवीनीकरण किया जाता है।

public class RandomGenerator { 
    private final ExecutorService generator = Executors.newSingleThreadExecutor(new ThreadFactory() { 
     @Override 
     public Thread newThread(Runnable r) { 
      Thread t = new Thread(r, "generator"); 
      t.setDaemon(true); 
      return t; 
     } 
    }); 
    private final Exchanger<double[][]> exchanger = new Exchanger<>(); 
    private double[][] buffer; 
    private int nextRow = Integer.MAX_VALUE; 

    public RandomGenerator(final int rows, final int columns) { 
     buffer = new double[rows][columns]; 
     generator.submit(new Callable<Void>() { 
      @Override 
      public Void call() throws Exception { 
       Random random = new Random(); 
       double[][] buffer2 = new double[rows][columns]; 
       while (!Thread.interrupted()) { 
        for (int r = 0; r < rows; r++) 
         for (int c = 0; c < columns; c++) 
          buffer2[r][c] = random.nextGaussian(); 
        buffer2 = exchanger.exchange(buffer2); 
       } 
       return null; 
      } 
     }); 
    } 

    public double[] nextArray() throws InterruptedException { 
     if (nextRow >= buffer.length) { 
      buffer = exchanger.exchange(buffer); 
      nextRow = 0; 
     } 
     return buffer[nextRow++]; 
    } 
} 

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

समानांतर में छोटे, स्वतंत्र प्राइमेटिव की बड़ी धाराओं को उत्पन्न करने और उन्हें एक थ्रेड से उपभोग करने के सामान्य मामले को कैसे संभालना है।

मैं एक Exchanger<double[][]> का प्रयोग करेंगे पृष्ठभूमि में मान के रूप में (ज्यादा जीसी भूमि के ऊपर के बिना)

+0

@ ग्रे भी सच है। प्रश्न का उत्तर देने के करीब आना। –

+0

पृष्ठभूमि में बैचों में यादृच्छिक संख्या उत्पन्न करने के लिए एक्सचेंजर का उपयोग करने का एक उदाहरण जोड़ा गया। –

+1

संचार के लिए एक एक्सचेंजर का संयोजन और रैंडों की लंबी धाराओं को चंकने से प्रदर्शन के लिए बहुत कुछ किया गया। धन्यवाद। – Bryce

5

आपके प्रदर्शन के साथ समस्या यह प्रतीत होती है कि व्यक्तिगत नौकरियां बहुत छोटी हैं, इसलिए ज्यादातर समय सिंक्रनाइज़ेशन और नौकरियों की कतार में काम करने में व्यतीत होता है। विचार करने की एक बात छोटी नौकरियों की एक बड़ी धारा उत्पन्न करने के लिए नहीं बल्कि प्रत्येक कामकाजी धागे को नौकरियों का एक मध्यम आकार का संग्रह देने के लिए उत्तर देने के लिए उत्तर देने वाली बात है।

उदाहरण के लिए, पहले थ्रेड के साथ अपने लूप के माध्यम से पुनरावृत्ति # 0 करने के बजाय, अगला थ्रेड पुनरावृत्ति # 1 कर रहा है ... ... मेरे पास पहला धागा # 999 या # 99 के माध्यम से पुनरावृत्तियों # 0 होगा। उन्हें स्वतंत्र रूप से काम करना चाहिए और उनकी गणना के उत्तर के साथ Job कक्षा को एनोटेट करना चाहिए। फिर अंत में वे Future के रूप में समाप्त की गई नौकरियों के पूरे संग्रह को वापस कर सकते हैं।

आपका Job वर्ग निम्नलिखित की तरह कुछ हो सकता है:

public class Job { 
    Collection<RealVector> dataCollection; 
    Collection<SomeAnswer> answerCollection = new ArrayList<SomeAnswer>(); 
    public void run() { 
     for (RealVector d : dataCollection) { 
      // do the magic work on the vector 
      while(!converged){ 
       ... 
      } 
      // put the associated "answer" in another collection 
      answerCollection.add(someAnswer); 
     } 
    } 
} 
+0

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

+0

फिर, यहां लक्ष्य सिंक्रनाइज़ेशन @ ब्रिस की मात्रा को कम करना है। हो सकता है कि प्रत्येक थ्रेड में 'थ्रेडलोकल' हो, उसके रैंड्स के साथ कोई भी बर्बाद नहीं होगा? या केवल एक काम तक रैंड्स कचरे को कम करने के लिए खंड आकार को बढ़ाएं, इससे कोई फर्क नहीं पड़ता कि इससे रन पर कोई फर्क नहीं पड़ता। – Gray

+0

मुझे इस 5-100 तत्वों पर @Gray का बैक अप लेना है, जो अंतर-थ्रेड कॉम के लिए कुशलता से कम है। यदि आप इसे 1000 से अधिक कारक कह सकते हैं, तो चीजों को सुधारना चाहिए। –