Подтвердить что ты не робот

В какой момент обертывание FileOutputStream с BufferedOutputStream имеет смысл с точки зрения производительности?

У меня есть модуль, который отвечает за чтение, обработку и запись байтов на диск. Байты входят в UDP и, после сборки отдельных датаграмм, последний массив байтов, который обрабатывается и записывается на диск, обычно составляет от 200 до 500 000 байт. Случайно, будут байтовые массивы, которые после сборки превышают 500 000 байт, но они относительно редки.

В настоящее время я использую метод FileOutputStream write(byte\[\]). Я также экспериментирую с оберткой FileOutputStream в BufferedOutputStream, включая конструктор, который принимает размер буфера в качестве параметра.

Похоже, что использование BufferedOutputStream имеет тенденцию к немного лучшей производительности, но я только начал экспериментировать с разными размерами буфера. У меня есть только ограниченный набор выборочных данных для работы (два набора данных из пробных прогонов, которые я могу транслировать через мое приложение). Есть ли общее правило большого пальца, которое я могу применить, чтобы попытаться вычислить оптимальные размеры буфера для уменьшения записи на диск и максимизировать производительность записи на диске, учитывая информацию, которую я знаю о данных, которые я пишу?

4b9b3361

Ответ 1

BufferedOutputStream помогает, когда записи меньше размера буфера, например. 8 КБ. Для больших записей это не помогает и не делает это намного хуже. Если ВСЕ ваши записи превышают размер буфера или вы всегда очищаете() после каждой записи, я бы не использовал буфер. Однако, если хорошая часть ваших записей меньше, чем размер буфера, и вы не используете flush() каждый раз, его стоит иметь.

Возможно, вы увеличиваете размер буфера до 32 КБ или более, что дает вам незначительное улучшение или ухудшает его. YMMV


Вы можете найти код для BufferedOutputStream.write полезным

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

Ответ 2

В последнее время я пытаюсь изучить производительность ввода-вывода. Из того, что я наблюдал, прямое письмо к FileOutputStream привело к лучшим результатам; который я приписал FileOutputStream для обычного вызова для write(byte[], int, int). Более того, я также заметил, что когда BufferedOutputStream латентность начинает сходиться к прямой FileOutputStream, она колеблется намного больше, т.е. Она может резко даже удвоиться (я еще не смог выяснить, почему).

P.S. Я использую Java 8 и не могу сейчас прокомментировать, будут ли мои наблюдения сохранены для предыдущих версий java.

Здесь код, который я тестировал, где мой вход был размером ~ 10 КБ

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

ВЫВОД:

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]