example java file-io io fileoutputstream bufferedoutputstream

java - example - ¿En qué punto tiene sentido envolver un FileOutputStream con un BufferedOutputStream, en términos de rendimiento?



bufferedoutputstream java (2)

Últimamente he estado tratando de explorar el rendimiento de IO. Por lo que he observado, escribir directamente en un FileOutputStream ha dado lugar a mejores resultados; que he atribuido a la llamada de write(byte[], int, int) nativa de FileOutputStream write(byte[], int, int) . Además, también he observado que cuando la latencia de BufferedOutputStream comienza a converger hacia la de FileOutputStream directo, fluctúa mucho más, es decir, puede duplicarse abruptamente (todavía no he podido averiguar por qué).

PD. Estoy usando Java 8 y no podré comentar en este momento si mis observaciones serán válidas para las versiones anteriores de Java.

Aquí está el código que probé, donde mi entrada fue un archivo ~ 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"))); } } } }

SALIDA:

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]

Tengo un módulo que es responsable de leer, procesar y escribir bytes en el disco. Los bytes entran a través de UDP y, después de que se ensamblan los datagramas individuales, la matriz de bytes final que se procesa y escribe en el disco suele tener entre 200 bytes y 500,000 bytes. Ocasionalmente, habrá conjuntos de bytes que, después del ensamblaje, tienen más de 500,000 bytes, pero estos son relativamente raros.

Actualmente estoy usando el método write(byte/[/]) FileOutputStream . También estoy experimentando con el FileOutputStream de FileOutputStream en un BufferedOutputStream , incluido el uso del constructor que acepta un tamaño de búfer como parámetro .

Parece que usar BufferedOutputStream tiende a un rendimiento ligeramente mejor, pero recién comencé a experimentar con diferentes tamaños de búfer. Solo tengo un conjunto limitado de datos de muestra para trabajar (dos conjuntos de datos de ejecuciones de muestra que puedo canalizar a través de mi aplicación). ¿Existe una regla empírica general que pueda aplicar para tratar de calcular los tamaños de búfer óptimos para reducir las grabaciones en disco y maximizar el rendimiento de la escritura del disco dada la información que conozco sobre los datos que estoy escribiendo?


BufferedOutputStream ayuda cuando las escrituras son más pequeñas que el tamaño del búfer, por ejemplo, 8 KB. Para escrituras más grandes no ayuda ni empeora las cosas. Si TODAS sus escrituras son más grandes que el tamaño del búfer o usted siempre vacía () después de cada escritura, no usaría un búfer. Sin embargo, si una buena parte de sus escrituras es menor que el tamaño del búfer y no utiliza flush () cada vez, vale la pena tener.

Puede encontrar que aumentar el tamaño del búfer a 32 KB o más le da una mejora marginal o lo empeora. YMMV

Puede encontrar útil el código para 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''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; }