44 votes

À partir de quel moment est-il judicieux d'envelopper un FileOutputStream avec un BufferedOutputStream, en termes de performances ?

J'ai un module qui est responsable de la lecture, du traitement et de l'écriture des octets sur le disque. Les octets arrivent par UDP et, après l'assemblage des datagrammes individuels, le tableau final d'octets qui est traité et écrit sur le disque est généralement compris entre 200 et 500 000 octets. Il arrive parfois que des tableaux d'octets dépassent 500 000 octets après assemblage, mais c'est relativement rare.

J'utilise actuellement le FileOutputStream 's write(byte\[\]) méthode . J'expérimente également l'enveloppement de la fonction FileOutputStream dans un BufferedOutputStream notamment en utilisant le constructeur qui accepte une taille de tampon comme paramètre .

Il semble que l'utilisation du BufferedOutputStream tend vers des performances légèrement meilleures, mais je viens juste de commencer à expérimenter avec différentes tailles de tampon. Je ne dispose que d'un ensemble limité d'échantillons de données pour travailler (deux ensembles de données provenant de séries d'échantillons que je peux faire passer dans mon application). Existe-t-il une règle générale que je pourrais appliquer pour essayer de calculer les tailles de tampon optimales pour réduire les écritures sur le disque et maximiser les performances de l'écriture sur le disque, compte tenu des informations que je connais sur les données que j'écris ?

32voto

Peter Lawrey Points 229686

BufferedOutputStream aide lorsque les écritures sont plus petites que la taille du tampon, par exemple 8 Ko. Pour les écritures plus importantes, cela n'aide pas et n'aggrave pas la situation. Si TOUTES vos écritures sont supérieures à la taille du tampon ou si vous utilisez toujours la fonction flush() après chaque écriture, je n'utiliserais pas de tampon. Cependant, si une bonne partie de vos écritures sont inférieures à la taille du tampon et que vous n'utilisez pas toujours flush(), cela vaut la peine de l'avoir.

Il se peut que l'augmentation de la taille de la mémoire tampon à 32 Ko ou plus ne vous apporte qu'une amélioration marginale, ou aggrave la situation. YMMV


Vous pouvez trouver le code pour BufferedOutputStream.write utile

/**
 * Writes <code>len</code> bytes from the specified byte array
 * starting at offset <code>off</code> to this buffered output stream.
 *
 * <p> Ordinarily this method stores bytes from the given array into this
 * stream's buffer, flushing the buffer to the underlying output stream as
 * needed.  If the requested length is at least as large as this stream's
 * buffer, however, then this method will flush the buffer and write the
 * bytes directly to the underlying output stream.  Thus redundant
 * <code>BufferedOutputStream</code>s will not copy data unnecessarily.
 *
 * @param      b     the data.
 * @param      off   the start offset in the data.
 * @param      len   the number of bytes to write.
 * @exception  IOException  if an I/O error occurs.
 */
public synchronized void write(byte b[], int off, int len) throws IOException {
    if (len >= buf.length) {
        /* If the request length exceeds the size of the output buffer,
           flush the output buffer and then write the data directly.
           In this way buffered streams will cascade harmlessly. */
        flushBuffer();
        out.write(b, off, len);
        return;
    }
    if (len > buf.length - count) {
        flushBuffer();
    }
    System.arraycopy(b, off, buf, count, len);
    count += len;
}

0voto

Dev Amitabh Points 119

J'ai récemment essayé d'explorer les performances IO. D'après ce que j'ai observé, l'écriture directe dans un fichier de type FileOutputStream a permis d'obtenir de meilleurs résultats, ce que j'ai attribué à FileOutputStream de l'appel natif de write(byte[], int, int) . De plus, j'ai également observé que lorsque BufferedOutputStream Le temps de latence de l'opérateur commence à converger vers celui de l'opérateur direct. FileOutputStream il fluctue beaucoup plus, c'est-à-dire qu'il peut même doubler brusquement (je n'ai pas encore réussi à trouver pourquoi).

P.S. J'utilise Java 8 et je ne suis pas en mesure de dire si mes observations s'appliquent aux versions précédentes de Java.

Voici le code que j'ai testé, où mon entrée était un fichier de ~10KB

public class WriteCombinationsOutputStreamComparison {
    private static final Logger LOG = LogManager.getLogger(WriteCombinationsOutputStreamComparison.class);

public static void main(String[] args) throws IOException {

    final BufferedInputStream input = new BufferedInputStream(new FileInputStream("src/main/resources/inputStream1.txt"), 4*1024);
    final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    int data = input.read();
    while (data != -1) {
        byteArrayOutputStream.write(data); // everything comes in memory
        data = input.read();
    }
    final byte[] bytesRead = byteArrayOutputStream.toByteArray();
    input.close();

    /*
     * 1. WRITE USING A STREAM DIRECTLY with entire byte array --> FileOutputStream directly uses a native call and writes
     */
    try (OutputStream outputStream = new FileOutputStream("src/main/resources/outputStream1.txt")) {
        final long begin = System.nanoTime();
        outputStream.write(bytesRead);
        outputStream.flush();
        final long end = System.nanoTime();
        LOG.info("Total time taken for file write, writing entire array [nanos=" + (end - begin) + "], [bytesWritten=" + bytesRead.length + "]");
        if (LOG.isDebugEnabled()) {
            LOG.debug("File reading result was: \n" + new String(bytesRead, Charset.forName("UTF-8")));
        }
    }

    /*
     * 2. WRITE USING A BUFFERED STREAM, write entire array
     */

    // changed the buffer size to different combinations --> write latency fluctuates a lot for same buffer size over multiple runs
    try (BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream("src/main/resources/outputStream1.txt"), 16*1024)) {
        final long begin = System.nanoTime();
        outputStream.write(bytesRead);
        outputStream.flush();
        final long end = System.nanoTime();
        LOG.info("Total time taken for buffered file write, writing entire array [nanos=" + (end - begin) + "], [bytesWritten=" + bytesRead.length + "]");
        if (LOG.isDebugEnabled()) {
            LOG.debug("File reading result was: \n" + new String(bytesRead, Charset.forName("UTF-8")));
        }
    }
}
}

SORTIE :

2017-01-30 23:38:59.064 [INFO] [main] [WriteCombinationsOutputStream] - Total time taken for file write, writing entire array [nanos=100990], [bytesWritten=11059]

2017-01-30 23:38:59.086 [INFO] [main] [WriteCombinationsOutputStream] - Total time taken for buffered file write, writing entire array [nanos=142454], [bytesWritten=11059]

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X