2016-01-04 9 views
7

स्कूल में असाइनमेंट के लिए मुझे एक साधारण प्रोग्राम बनाने के लिए कहा गया था जो 1000 टेक्स्ट फाइलें बनाता है, प्रत्येक यादृच्छिक राशि के साथ, गिनती है कि बहु-थ्रेड \ एकल प्रक्रिया के माध्यम से कितनी लाइनें हैं। उन फ़ाइलों को हटाने से।मल्टी थ्रेड एक प्रक्रिया से धीमी गति से चलाता है

अब परीक्षण के दौरान एक अजीब चीज होती है - सभी फाइलों की रैखिक गिनती हमेशा एक बहु-थ्रेडेड तरीके से गिनने से थोड़ा तेज होती है जिसने मेरे कक्षा सर्कल के भीतर काफी अकादमिक सिद्धांत सत्र को जन्म दिया है।

जब Scanner का उपयोग कर सभी फाइलों को पढ़ने के लिए, सब कुछ काम करता है के रूप में इरादा - 1000 फाइलों के आसपास 500ms रैखिक समय में पढ़ रहे हैं और 400 मि.से समय

पिरोया अभी तक जब मैं चारों ओर 110ms रैखिक और 130ms थ्रेड के लिए छोड़ BufferedReader बार का उपयोग करें।

कोड का कौन सा हिस्सा इस बाधा का कारण बनता है और क्यों?

संपादित करें: बस स्पष्ट करने के लिए, मैं नहीं पूछ रहा हूं कि ScannerBufferedReader से धीमा क्यों काम करता है।

पूर्ण संकलन में सक्षम कोड: (हालांकि आप फ़ाइल निर्माण पथ उत्पादन बदलना चाहिए)

import java.io.*; 
import java.util.Random; 
import java.util.Scanner; 

/** 
* Builds text files with random amount of lines and counts them with 
* one process or multi-threading. 
* @author Hazir 
*/// CLASS MATALA_4A START: 
public class Matala_4A { 

    /* Finals: */ 
    private static final String MSG = "Hello World"; 

    /* Privates: */ 
    private static int count; 
    private static Random rand; 

    /* Private Methods: */ /** 
    * Increases the random generator. 
    * @return The new random value. 
    */ 
    private static synchronized int getRand() { 
     return rand.nextInt(1000); 
    } 

    /** 
    * Increments the lines-read counter by a value. 
    * @param val The amount to be incremented by. 
    */ 
    private static synchronized void incrementCount(int val) { 
     count+=val; 
    } 

    /** 
    * Sets lines-read counter to 0 and Initializes random generator 
    * by the seed - 123. 
    */ 
    private static void Initialize() { 
     count=0; 
     rand = new Random(123); 
    } 

    /* Public Methods: */ /** 
    * Creates n files with random amount of lines. 
    * @param n The amount of files to be created. 
    * @return String array with all the file paths. 
    */ 
    public static String[] createFiles(int n) { 
     String[] array = new String[n]; 
     for (int i=0; i<n; i++) { 
      array[i] = String.format("C:\\Files\\File_%d.txt", i+1); 
      try ( // Try with Resources: 
        FileWriter fw = new FileWriter(array[i]); 
        PrintWriter pw = new PrintWriter(fw); 
        ) { 
       int numLines = getRand(); 
       for (int j=0; j<numLines; j++) pw.println(MSG); 
      } catch (IOException ex) { 
       System.err.println(String.format("Failed Writing to file: %s", 
         array[i])); 
      } 
     } 
     return array; 
    } 

    /** 
    * Deletes all the files who's file paths are specified 
    * in the fileNames array. 
    * @param fileNames The files to be deleted. 
    */ 
    public static void deleteFiles(String[] fileNames) { 
     for (String fileName : fileNames) { 
      File file = new File(fileName); 
      if (file.exists()) { 
       file.delete(); 
      } 
     } 
    } 

    /** 
    * Creates numFiles amount of files.<br> 
    * Counts how many lines are in all the files via Multi-threading.<br> 
    * Deletes all the files when finished. 
    * @param numFiles The amount of files to be created. 
    */ 
    public static void countLinesThread(int numFiles) { 
     Initialize(); 
     /* Create Files */ 
     String[] fileNames = createFiles(numFiles); 
     Thread[] running = new Thread[numFiles]; 
     int k=0; 
     long start = System.currentTimeMillis(); 
     /* Start all threads */ 
     for (String fileName : fileNames) { 
      LineCounter thread = new LineCounter(fileName); 
      running[k++] = thread; 
      thread.start(); 
     } 
     /* Join all threads */ 
     for (Thread thread : running) { 
      try { 
       thread.join(); 
      } catch (InterruptedException e) { 
       // Shouldn't happen. 
      } 
     } 
     long end = System.currentTimeMillis(); 
     System.out.println(String.format("threads time = %d ms, lines = %d", 
       end-start,count)); 
     /* Delete all files */ 
     deleteFiles(fileNames); 
    } 

    @SuppressWarnings("CallToThreadRun") 
    /** 
    * Creates numFiles amount of files.<br> 
    * Counts how many lines are in all the files in one process.<br> 
    * Deletes all the files when finished. 
    * @param numFiles The amount of files to be created. 
    */ 
    public static void countLinesOneProcess(int numFiles) { 
     Initialize(); 
     /* Create Files */ 
     String[] fileNames = createFiles(numFiles); 
     /* Iterate Files*/ 
     long start = System.currentTimeMillis(); 
     LineCounter thread; 
     for (String fileName : fileNames) { 
      thread = new LineCounter(fileName); 
      thread.run(); // same process 
     } 
     long end = System.currentTimeMillis(); 
     System.out.println(String.format("linear time = %d ms, lines = %d", 
       end-start,count)); 
     /* Delete all files */ 
     deleteFiles(fileNames); 
    } 

    public static void main(String[] args) { 
     int num = 1000; 
     countLinesThread(num); 
     countLinesOneProcess(num); 
    } 

    /** 
    * Auxiliary class designed to count the amount of lines in a text file. 
    */// NESTED CLASS LINECOUNTER START: 
    private static class LineCounter extends Thread { 

     /* Privates: */ 
     private String fileName; 

     /* Constructor: */ 
     private LineCounter(String fileName) { 
      this.fileName=fileName; 
     } 

     /* Methods: */ 

     /** 
     * Reads a file and counts the amount of lines it has. 
     */ @Override 
     public void run() { 
      int count=0; 
      try (// Try with Resources: 
        FileReader fr = new FileReader(fileName); 
        //Scanner sc = new Scanner(fr); 
        BufferedReader br = new BufferedReader(fr); 
        ) { 
       String str; 
       for (str=br.readLine(); str!=null; str=br.readLine()) count++; 
       //for (; sc.hasNext(); sc.nextLine()) count++; 
       incrementCount(count); 
      } catch (IOException e) { 
       System.err.println(String.format("Failed Reading from file: %s", 
       fileName));    
      } 
     } 
    } // NESTED CLASS LINECOUNTER END; 
} // CLASS MATALA_4A END; 
+1

@kstandell nay, 'countLinesOneProcess()' बहु-थ्रेडेड नहीं है। यह थ्रेड के '.run()' फ़ंक्शन को '.start()' के बिना कॉल करता है, इसलिए यह केवल नियमित वर्ग विशिष्ट विधि के रूप में चलता है। –

+0

यह जावा नहीं है कि आप जावा बेंचमार्क कैसे करते हैं! ये संख्या पूरी तरह से व्यर्थ हैं। –

+0

क्या एक जटिल होमवर्क – Machado

उत्तर

5

वहाँ विभिन्न कारकों हो सकता है:

  • सबसे महत्वपूर्ण कई से डिस्क पहुँच से परहेज है एक ही समय में धागे (लेकिन जब से आप एसएसडी पर हैं, तो आप इससे दूर हो सकते हैं)। एक सामान्य हार्डडिस्क पर, एक फ़ाइल से दूसरे में स्विच करने से आपको 10 एमएमएस समय की तलाश हो सकती है (इस पर निर्भर करता है कि डेटा कैसे कैश किया जाता है)।

  • 1000 धागे बहुत अधिक हैं, कोर की संख्या का उपयोग करने का प्रयास करें * 2. बहुत अधिक समय केवल स्विचिंग संदर्भ खो जाएंगे।

  • थ्रेड पूल का उपयोग करने का प्रयास करें। कुल समय 110ms और 130ms के बीच है, इसका हिस्सा धागे बनाने से होगा।

  • सामान्य रूप से परीक्षण में कुछ और काम करें। समय 110ms हमेशा सटीक नहीं है। यह भी निर्भर करता है कि उस समय अन्य प्रक्रियाओं या धागे किस प्रकार चल रहे हैं।

  • कोशिश अपने परीक्षण का क्रम बदलने के लिए देखने के लिए अगर यह एक फर्क नहीं पड़ता (कैशिंग एक महत्वपूर्ण कारक हो सकता है)

    countLinesThread(num); 
    countLinesOneProcess(num); 
    

इसके अलावा, सिस्टम के आधार पर, currentTimeMillis() एक संकल्प हो सकता है 10 से 15ms के। तो यह समय कम रनों के लिए बहुत सटीक नहीं है।

long start = System.currentTimeMillis(); 
long end = System.currentTimeMillis(); 
+0

परीक्षणों के क्रम को बदलने से पूरे गिनती ने स्थिर तरीके से थ्रेडिंग के लिए तेज़ समय उत्पन्न किया है। क्या आप मुझे इस बात का जिक्र करेंगे कि किस तरह का "कैशिंग" होता है और मैं इसे कहां पढ़ सकता हूं? –

+1

डिस्क का कैशिंग –

+1

@GiladMitrani पढ़ता है - यह ओएस डिस्क कैश है। पहली पहुंच में, डेटा या डिस्क पेज कैश में पढ़े जाते हैं। इसके बाद सभी पहुंच सीधे कैश से पढ़ी जाएंगी, जो बहुत तेज है (विशेष रूप से जब सामान्य हार्डडिस्क का उपयोग करते हैं) –

10

टोंटी डिस्क है।

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

मल्टीथ्रेड का उपयोग केवल तभी दिलचस्प है जब आपको अलग-अलग स्रोतों (उदाहरण के लिए नेटवर्क और डिस्क, या दो अलग-अलग डिस्क, या कई नेटवर्क स्ट्रीम) पर लंबे I/O संचालन के लिए अपने काम को विभाजित करने की आवश्यकता हो या यदि आपके पास सीपीयू गहन है ऑपरेशन जिसे विभिन्न कोरों के बीच विभाजित किया जा सकता है।

याद रखें कि एक अच्छा बहु सूत्रण कार्यक्रम आप हमेशा की जरूरत को ध्यान में लेने के लिए के लिए: सूत्र

  • लंबे आई/ओ संचालन समानांतर में किया जा सकता है या नहीं
  • गहन CPU के बीच

    • स्विच संदर्भ समय गणना के लिए समय मौजूद है या नहीं
    • सीपीयू कंप्यूटेशंस को सबप्रोबलेम्स में विभाजित किया जा सकता है या
    • धागे के बीच डेटा साझा करने के लिए जटिलता (सेमफोरस या सिंक्रोनिज़ति पर)
    • किसी एकल थ्रेड आवेदन
  • +1

    भले ही मैं एक एसएसडी ड्राइव का उपयोग कर रहा हूं? –

    +1

    @GiladMitrani यह I/O की सटीक संरचना पर निर्भर करता है। यदि डिस्क को पढ़ना प्रसंस्करण से तेज़ है, तो शायद एक लाभ होगा। हालांकि, ज्यादातर मामलों में, एसएसडी भी लाइन गिनती के रूप में सरल प्रक्रिया के मुकाबले धीमे होते हैं, और आईओओ शेड्यूलिंग में ओएस ओवरहेड आपको मल्टीथ्रेडिंग से प्राप्त गैर-लाभ की तुलना में अच्छा नहीं करेगा। – hexafraction

    +0

    यदि गणना समय बहुत तेज है (डिस्क तक पहुंच की तुलना में) हाँ कई धागे का उपयोग करके कोई प्रकोप नहीं है। –

    1

    इस्तेमाल किया धागे की संख्या बहुत महत्वपूर्ण है की तुलना में पढ़ने, लिखने और प्रबंधित एक multithread कोड करने के लिए मुश्किल। 1000 थ्रेड के बीच स्विच करने की कोशिश करने वाली एक प्रक्रिया (आपने प्रति फ़ाइल एक नया धागा बनाया है) शायद धीमे होने का मुख्य कारण है।

    कोशिश के 10 सूत्र का कहना है कि 1000 फाइलों को पढ़ने के लिए जाने का उपयोग करने के, तो आप ध्यान देने योग्य गति वृद्धि देखेंगे

    +0

    फ़ाइल प्रति थ्रेड का उपयोग असाइनमेंट द्वारा निर्धारित किया जाता है।फिर भी, जब मैंने 'स्कैनर' का उपयोग किया, धीमी विधियों, यहां तक ​​कि तथ्य यह है कि मैं 1000 धागे का उपयोग कर रहा था, मुझे कुछ समय –

    +1

    @ गिलादमित्रानी अच्छी तरह से अपने साधारण साथी का कारण बनता है, कारण स्कैनर का उपयोग मल्टी थ्रेडेड में तेज़ है क्योंकि 'स्विचिंग थ्रेड' है फ़ाइल पढ़ने से वास्तव में तेज़। – nafas

    0

    वास्तविक समय गणना के लिए आवश्यक समय मैं के लिए आवश्यक की तुलना में नगण्य है, तो/हे, संभावित बहु threding लाभ नगण्य रूप में अच्छी तरह कर रहे हैं: एक धागा अच्छी तरह से परिपूर्ण करने के लिए आई/ओ में सक्षम है और होगा फिर बहुत त्वरित गणना करें; अधिक धागे चीजों को तेज नहीं कर सकते हैं। इसके बजाए, सामान्य थ्रेडिंग ओवरहेड लागू होंगे, साथ ही संभावित रूप से I/O कार्यान्वयन में लॉकिंग जुर्माना वास्तव में थ्रूपुट को कम करेगा।

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

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