2009-09-02 32 views
9

Tôi đã triển khai một lớp IO nhỏ, có thể đọc từ nhiều tệp giống nhau trên các đĩa khác nhau (ví dụ: hai đĩa cứng chứa cùng một tệp). Trong trường hợp tuần tự, cả hai đĩa đều đọc trung bình 60MB/s trên tệp, nhưng khi tôi thực hiện xen kẽ (ví dụ: 4k đĩa 1, 4k đĩa 2 rồi kết hợp), tốc độ đọc hiệu quả giảm xuống 40MB/s thay vì tăng?Tập tin song song xen kẽ đọc chậm hơn tuần tự đọc?

Bối cảnh: Win 7 + JDK 7b70, RAM 2GB, tệp kiểm tra 2.2 GB. Về cơ bản, tôi cố gắng bắt chước ReadyBoost và RAID x của Win7 theo kiểu người nghèo.

Trong trái tim, khi đọc() được phát hành cho lớp, nó tạo ra hai runnables với các hướng dẫn để đọc một RandomAccessFile được mở trước từ một vị trí và độ dài nhất định. Sử dụng một dịch vụ thi hành và các cuộc gọi Future.get(), khi cả hai kết thúc, dữ liệu đọc được sao chép vào một bộ đệm chung và trả về cho người gọi.

Có lỗi nào trong quá trình tiếp cận của tôi không? (Ví dụ, cơ chế điều hành bộ nhớ đệm sẽ luôn luôn chống lại?)

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 trong mảng số liệu chứa các phép đo tốc độ đọc để xác định thích nghi các kích thước bộ đệm của các kênh đầu vào khác nhau - trong thử nghiệm của tôi với alpha = 0 và readSpeed = 1 kết quả phân phối bình đẳng)

Sửa tôi chạy một thử nghiệm không vướng (ví dụ như đọc hai tập tin một cách độc lập trong chủ đề riêng biệt.) và tôi đã có một tốc độ hiệu quả kết hợp của 110MB/s.

Chỉnh sửa2 Tôi đoán tôi biết tại sao điều này lại xảy ra.

Khi tôi đọc song song và theo thứ tự, nó không được đọc tuần tự cho các đĩa, mà là đọc-bỏ-đọc-bỏ mẫu do sự xen kẽ (và có thể được tìm kiếm bảng phân bổ). Điều này về cơ bản làm giảm tốc độ đọc hiệu quả cho mỗi đĩa đến một nửa hoặc tồi tệ hơn.

+0

Đây là một vấn đề thú vị và tốt cho bạn khi tìm giải pháp. Tôi nghĩ bạn nên viết giải pháp như một câu trả lời và chấp nhận câu trả lời của riêng bạn. – Guss

Trả lời

3

Như bạn đã nói, đọc tuần tự trên đĩa nhanh hơn nhiều so với mẫu đọc bỏ qua. Đĩa cứng có khả năng băng thông cao khi đọc tuần tự, nhưng thời gian tìm kiếm (độ trễ) là tốn kém.

Thay vì lưu trữ một bản sao của tệp trong mỗi đĩa, hãy thử lưu trữ khối i của tệp trên đĩa i (mod 2). Bằng cách này bạn có thể đọc từ cả hai đĩa tuần tự và kết hợp lại kết quả trong bộ nhớ.

+0

Đó cũng là ý tưởng của tôi và nó hoạt động. – akarnokd

0

Nếu bạn chắc chắn rằng bạn thực hiện không quá một lần đọc mỗi đĩa (nếu không bạn sẽ có nhiều đĩa bị thiếu), bạn vẫn tạo tranh chấp trên các phần khác trong máy tính - bus, bộ điều khiển đột kích (nếu có) và Sớm.

+0

Không, nó không phải là trường hợp tranh chấp xe buýt. – akarnokd

2

Nếu bạn muốn đọc song song, hãy chia đọc thành hai lần đọc tuần tự. Tìm nửa chừng và đọc nửa đầu từ tệp thứ nhất và nửa thứ hai từ tệp thứ hai.

+0

Cảm ơn, tôi đã suy nghĩ lại vấn đề cơ sở và tìm thấy một cách tốt hơn để đạt được những cải thiện về tốc độ. – akarnokd

Các vấn đề liên quan