10
votes

Le fichier parallèle entrelacé lu lentement que la lecture séquentielle?

J'ai mis en place une petite classe IO, qui peut lire à partir de plusieurs fichiers et même sur des disques différents (par exemple deux disques durs contenant le même fichier). Dans le cas séquentiel, les deux disques lus 60 Mo / s en moyenne sur le fichier, mais quand je fais un intercalés (par exemple 4k disque 1, 4k disque 2 puis moissonneuse-batteuse), la vitesse de lecture efficace est réduite à 40 Mo / s au lieu d'augmenter?

Contexte: Win 7 + 7b70 JDK, 2 Go de RAM, fichier de test 2.2GB. En gros, j'essaie de ReadyBoost de mimique Win7 et RAID de x dans la mode d'un pauvre homme. P>

Au coeur, lorsqu'une lecture () est délivré à la classe, il crée deux runnables avec des instructions pour lire un pré RandomAccessFile ouvert à partir d'une certaine position et la longueur. L'utilisation d'un service d'exécuteur testamentaire et les appels Future.get (), lorsque les deux finition, la lecture de données est copiée dans un tampon commun et renvoyé à l'appelant. P>

Y at-il une erreur dans mon approche conceptionnelle? (Par exemple, le mécanisme de mise en cache du système d'exploitation sera toujours Contrer?) P>

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;
}


1 commentaires

C'est un problème intéressant et bon pour vous pour trouver la solution. Je pense que vous devriez écrire la solution comme réponse et accepter votre propre réponse.


4 Réponses :


0
votes

Si vous êtes sûr que vous n'effectuez pas plus d'une lecture par disque (sinon, vous aurez de nombreuses missions de disque), vous créez toujours des affections sur d'autres pièces de l'ordinateur - le bus RAID, le contrôleur RAID (s'il existe) et ainsi sur.


1 commentaires

Non, ce n'est pas le cas de la conflit de bus.



2
votes

Si vous souhaitez effectuer une lecture parallèle, rompez la lecture en deux lectures séquentielles. Trouvez le point à mi-chemin et lisez la première moitié du premier fichier et la seconde moitié du second fichier.


1 commentaires

Merci, j'ai déjà repensé la question de base et j'ai trouvé un meilleur moyen d'atteindre les améliorations de la vitesse.



3
votes

Comme vous l'avez dit, une lecture séquentielle sur un disque est beaucoup plus rapide qu'un motif de saut à lecture-lecture. Les disques durs sont capables d'une bande passante élevée lors de la lecture de manière séquentielle, mais le temps de recherche (latence) est coûteux.

Au lieu de stocker une copie du fichier dans chaque disque, essayez de stocker le bloc I du fichier sur le disque I (mod 2). De cette façon, vous pouvez lire des deux disques séquentiellement et recombiner le résultat en mémoire.


0 commentaires

0
votes

Peut-être http://stxxl.sourceforge.net/ pourrait également vous intéresser.


0 commentaires