Чередование параллельного файла чтение медленнее, чем последовательное чтение?
Я реализовал небольшой класс ввода-вывода, который может читать несколько файлов на разных дисках (например, два жестких диска, содержащие один и тот же файл). В последовательном случае оба диска читают в среднем 60 МБ / с по файлу, но когда я делаю чередование (например, 4 КБ диск 1, 4 КБ диск 2 затем объединяем), эффективная скорость чтения уменьшается до 40 МБ / с вместо увеличения?
Контекст: Win 7 + JDK 7b70, 2 ГБ ОЗУ, 2,2 ГБ тестовый файл. В основном я пытаюсь имитировать Win7 's ReadyBoost и RAID x у бедного человекамода.
В самом сердце, когда read () выдается классу, он создает два исполняемых файла с инструкциями для чтения предварительно открытого RandomAccessFile с определенной позиции и длины. Используя службу executor и вызовы Future.get (), когда оба завершаются, считанные данные копируются в общий буфер и возвращаются вызывающей стороне.
Есть ли концептуальная ошибка в моем подходе? (Например, механизм кэширования ОС всегда будет противодействовать?)
protected List waitForAll(List futures)
throws MultiIOException {
MultiIOException mex = null;
int i = 0;
List result = new ArrayList(futures.size());
for (Future 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 futures = new LinkedList();
// read in parallel
for (int i = 0; i < metrics.length; i++) {
final int j = i;
futures.add(exec.submit(new Callable() {
@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 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 в массиве метрик содержит измерения скорости чтения для адаптивного определения размеров буфера различных входных каналов - в моем тесте с равным распределением результатов для alpha = 0 и readSpeed = 1)
редактировать Я провел не запутанный тест (например, прочитал два файла независимо в отдельных потоках.) И я 'у нас суммарная эффективная скорость 110 МБ / с.
Edit2 Я думаю, я знаю, почему это происходит.
Когда я читаю параллельно и последовательно, это не последовательное чтение дисков, а скорее шаблон чтение-пропуск-чтение-пропуск из-за чередования (и, возможно, пронизанного поиском в таблице размещения). Это в основном снижает эффективную скорость чтения на диск до половины или хуже.