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.
Đâ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