Чередование параллельного файла чтение медленнее, чем последовательное чтение?

Я реализовал небольшой класс ввода-вывода, который может читать несколько файлов на разных дисках (например, два жестких диска, содержащие один и тот же файл). В последовательном случае оба диска читают в среднем 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 Я думаю, я знаю, почему это происходит.

Когда я читаю параллельно и последовательно, это не последовательное чтение дисков, а скорее шаблон чтение-пропуск-чтение-пропуск из-за чередования (и, возможно, пронизанного поиском в таблице размещения). Это в основном снижает эффективную скорость чтения на диск до половины или хуже.

Ответы на вопрос(4)

Ваш ответ на вопрос