2009-09-02 6 views
9

मैंने एक छोटी आईओ कक्षा लागू की है, जो विभिन्न डिस्क पर एकाधिक और एक ही फाइलों से पढ़ सकता है (उदाहरण के लिए एक ही फाइल वाली दो हार्ड डिस्क)। अनुक्रमिक मामले में, दोनों डिस्क फ़ाइल पर औसत में 60 एमबी/एस पढ़ते हैं, लेकिन जब मैं एक इंटरलीव किया जाता हूं (उदा। 4k डिस्क 1, 4k डिस्क 2 फिर गठबंधन), प्रभावी पढ़ने की गति बढ़ने के बजाय 40 एमबी/एस तक कम हो जाती है?इंटरलीव किए गए समांतर फ़ाइल अनुक्रमिक पढ़ने से धीमी गति से पढ़ी जाती है?

संदर्भ: विन 7 + जेडीके 7 बी 70, 2 जीबी रैम, 2.2 जीबी परीक्षण फ़ाइल। असल में, मैं Win7 के ReadyBoost और RAID x को एक गरीब व्यक्ति के फैशन में नकल करने का प्रयास करता हूं।

दिल में, जब कक्षा में एक पढ़ा() जारी किया जाता है, तो यह एक निश्चित स्थिति और लंबाई से पूर्व-खोले गए RandomAccessFile को पढ़ने के निर्देशों के साथ दो रननेबल बनाता है। निष्पादक सेवा और Future.get() कॉल का उपयोग करते समय, दोनों समाप्त होने पर, डेटा पढ़ने को एक आम बफर में कॉपी किया जाता है और कॉलर पर वापस आ जाता है।

क्या मेरे दृष्टिकोण में कोई अवधारणात्मक त्रुटि है? (उदाहरण के लिए, ओएस कैशिंग तंत्र हमेशा प्रतिक्रिया करेगा?)

protected <T> List<T> waitForAll(List<Future<T>> futures) 
throws MultiIOException { 
    MultiIOException mex = null; 
    int i = 0; 
    List<T> result = new ArrayList<T>(futures.size()); 
    for (Future<T> f : futures) { 
     try { 
      result.add(f.get()); 
     } catch (InterruptedException ex) { 
      if (mex == null) { 
       mex = new MultiIOException(); 
      } 
      mex.exceptions.add(new ExceptionPair(metrics[i].file, ex)); 
     } catch (ExecutionException ex) { 
      if (mex == null) { 
       mex = new MultiIOException(); 
      } 
      mex.exceptions.add(new ExceptionPair(metrics[i].file, ex)); 
     } 
     i++; 
    } 
    if (mex != null) { 
     throw mex; 
    } 
    return result; 
} 

public int read(long position, byte[] output, int start, int length) 
throws IOException { 
    if (start < 0 || start + length > output.length) { 
     throw new IndexOutOfBoundsException(
     String.format("start=%d, length=%d, output=%d", 
     start, length, output.length)); 
    } 
    // compute the fragment sizes and positions 
    int result = 0; 
    final long[] positions = new long[metrics.length]; 
    final int[] lengths = new int[metrics.length]; 
    double speedSum = 0.0; 
    double maxValue = 0.0; 
    int maxIndex = 0; 
    for (int i = 0; i < metrics.length; i++) { 
     speedSum += metrics[i].readSpeed; 
     if (metrics[i].readSpeed > maxValue) { 
      maxValue = metrics[i].readSpeed; 
      maxIndex = i; 
     } 
    } 
    // adjust read lengths 
    int lengthSum = length; 
    for (int i = 0; i < metrics.length; i++) { 
     int len = (int)Math.ceil(length * metrics[i].readSpeed/speedSum); 
     lengths[i] = (len > lengthSum) ? lengthSum : len; 
     lengthSum -= lengths[i]; 
    } 
    if (lengthSum > 0) { 
     lengths[maxIndex] += lengthSum; 
    } 
    // adjust read positions 
    long positionDelta = position; 
    for (int i = 0; i < metrics.length; i++) { 
     positions[i] = positionDelta; 
     positionDelta += (long)lengths[i]; 
    }   
    List<Future<byte[]>> futures = new LinkedList<Future<byte[]>>(); 
    // read in parallel 
    for (int i = 0; i < metrics.length; i++) { 
     final int j = i; 
     futures.add(exec.submit(new Callable<byte[]>() { 
      @Override 
      public byte[] call() throws Exception { 
       byte[] buffer = new byte[lengths[j]]; 
       long t = System.nanoTime(); 
       long t0 = t; 

       long currPos = metrics[j].handle.getFilePointer(); 
       metrics[j].handle.seek(positions[j]); 
       t = System.nanoTime() - t; 
       metrics[j].seekTime = t * 1024.0 * 1024.0/
        Math.abs(currPos - positions[j])/1E9 ; 

       int c = metrics[j].handle.read(buffer); 
       t0 = System.nanoTime() - t0; 
       // adjust the read speed if we read something 
       if (c > 0) { 
        metrics[j].readSpeed = (alpha * c * 1E9/t0/1024/1024 
        + (1 - alpha) * metrics[j].readSpeed) ; 
       } 
       if (c < 0) { 
        return null; 
       } else 
       if (c == 0) { 
        return EMPTY_BYTE_ARRAY; 
       } else 
       if (c < buffer.length) { 
        return Arrays.copyOf(buffer, c); 
       } 
       return buffer; 
      } 
     })); 
    } 
    List<byte[]> data = waitForAll(futures); 
    boolean eof = true; 
    for (byte[] b : data) { 
     if (b != null && b.length > 0) { 
      System.arraycopy(b, 0, output, start + result, b.length); 
      result += b.length; 
      eof = false; 
     } else { 
      break; // the rest probably reached EOF 
     } 
    } 
    // if there was no data at all, we reached the end of file 
    if (eof) { 
     return -1; 
    } 
    sequentialPosition = position + (long)result; 

    // evaluate the fastest file to read 
    double maxSpeed = 0; 
    maxIndex = 0; 
    for (int i = 0; i < metrics.length; i++) { 
     if (metrics[i].readSpeed > maxSpeed) { 
      maxSpeed = metrics[i].readSpeed; 
      maxIndex = i; 
     } 
    } 
    fastest = metrics[maxIndex]; 
    return result; 
} 

(मैट्रिक्स सरणी में FileMetrics पढ़ने की गति की माप शामिल adaptively विभिन्न इनपुट चैनलों की बफ़र आकार निर्धारित करने के लिए - अल्फा = 0 और readSpeed ​​साथ अपने परीक्षण में = 1 परिणाम समान वितरण)

संपादित मैं एक गैर उलझ परीक्षण भाग गया (जैसे अलग धागे में स्वतंत्र रूप से दो फ़ाइलों को पढ़ने के।) और मैं 110MB/एस की एक संयुक्त प्रभावी गति मिल गया है।

संपादित 2 मुझे लगता है कि मुझे पता है कि यह क्यों हो रहा है।

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

+0

यह एक दिलचस्प समस्या है और समाधान खोजने के लिए आपके लिए अच्छा है। मुझे लगता है कि आपको समाधान को उत्तर के रूप में लिखना चाहिए और अपना जवाब स्वीकार करना चाहिए। – Guss

उत्तर

3

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

प्रत्येक डिस्क में फ़ाइल की प्रतिलिपि संग्रहीत करने के बजाय, डिस्क i (mod 2) पर फ़ाइल के ब्लॉक I को संग्रहीत करने का प्रयास करें। इस तरह आप अनुक्रमिक रूप से दोनों डिस्क से पढ़ सकते हैं और परिणाम को स्मृति में पुन: संयोजित कर सकते हैं।

+0

यह मेरा विचार भी था और यह काम करता है। – akarnokd

0

यदि आप सुनिश्चित हैं कि आप प्रति डिस्क एक से अधिक पढ़ने नहीं कर रहे हैं (अन्यथा आपके पास कई डिस्क याद आती हैं), तो आप अभी भी कंप्यूटर के अन्य हिस्सों पर विवाद बनाते हैं - बस, RAID नियंत्रक (यदि मौजूद है) और शीघ्र।

+0

नहीं, यह बस विवाद का मामला नहीं है। – akarnokd

2

यदि आप समानांतर पढ़ना चाहते हैं, तो दो अनुक्रमिक पढ़ने में पढ़ने को तोड़ दें। आधे रास्ते को ढूंढें और पहली फ़ाइल से पहली छमाही और दूसरी फ़ाइल से दूसरी छमाही पढ़ें।

+0

धन्यवाद, मैंने पहले ही आधार मुद्दे पर पुनर्विचार किया है और गति सुधार प्राप्त करने के लिए एक बेहतर तरीका पाया है। – akarnokd

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