2012-01-03 23 views
37

Tôi có một mô-đun chịu trách nhiệm đọc, xử lý và ghi byte vào đĩa. Các byte đến trên UDP và, sau khi các gói dữ liệu riêng lẻ được lắp ráp, mảng byte cuối cùng được xử lý và ghi vào đĩa thường là từ 200 byte đến 500.000 byte. Thỉnh thoảng, sẽ có các mảng byte, sau khi lắp ráp, là hơn 500.000 byte, nhưng chúng tương đối hiếm.Tại thời điểm nào gói một FileOutputStream với một BufferedOutputStream có ý nghĩa, về hiệu suất?

Tôi hiện đang sử dụng 's write(byte\[\]) method. Tôi cũng đang thử nghiệm gói FileOutputStream trong một số BufferedOutputStream, bao gồm sử dụng the constructor that accepts a buffer size as a parameter.

Dường như sử dụng BufferedOutputStream có xu hướng hướng tới hiệu suất tốt hơn một chút, nhưng tôi chỉ mới bắt đầu thử nghiệm với các kích thước bộ đệm khác nhau. Tôi chỉ có một tập hợp dữ liệu mẫu có giới hạn để làm việc với (hai tập dữ liệu từ chạy mẫu mà tôi có thể thông qua ứng dụng của mình). Có quy tắc chung nào mà tôi có thể áp dụng để thử tính toán kích thước bộ đệm tối ưu để giảm ghi đĩa và tối đa hóa hiệu năng ghi đĩa bằng thông tin mà tôi biết về dữ liệu tôi đang viết không?

Trả lời

28

BufferedOutputStream giúp khi ghi nhỏ hơn kích thước bộ đệm, ví dụ: 8 KB. Để viết lớn hơn nó không giúp đỡ cũng không làm cho nó tồi tệ hơn nhiều. Nếu tất cả các bài viết của bạn lớn hơn kích thước bộ đệm hoặc bạn luôn luôn flush() sau mỗi lần viết, tôi sẽ không sử dụng bộ đệm. Tuy nhiên nếu một phần tốt của bạn viết ít hơn kích thước bộ đệm và bạn không sử dụng flush() mỗi lần, giá trị của nó có.

Bạn có thể thấy việc tăng kích thước bộ đệm lên 32 KB hoặc lớn hơn cho phép bạn cải thiện biên, hoặc làm cho nó tồi tệ hơn. YMMV


Bạn có thể tìm ra mã cho BufferedOutputStream.write hữu ích

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

Cái gì tôi đã không tìm thấy được nêu ra - các kích thước bộ đệm mặc định của BufferedOutputStream là gì trong Java 6? Bạn đề cập đến 8KB - là mặc định trong Java? Các Javadocs cho 1.4.2 nói rằng bộ đệm là 512 byte, có nghĩa là hầu hết những gì tôi viết có xu hướng giảm từ 200 đến 400 byte cho mỗi mảng. Tuy nhiên, thông tin này được loại bỏ khỏi tài liệu Java 6. –

+3

@Thomas - [xem mã nguồn] (http://www.docjar.com/html/api/java/io/BufferedOutputStream.java.html#51), kích thước mặc định là 8192. Tôi cho rằng chúng loại bỏ các đặc điểm kỹ thuật kích thước mặc định để có thể thay đổi nó khi một "mặc định hợp lý nhất" mới xuất hiện. Nếu có một kích thước bộ đệm cụ thể là quan trọng, có thể bạn sẽ muốn xác định nó một cách rõ ràng. – gustafc

+1

@gustafc Cảm ơn. Tôi luôn quên rằng tôi có thể xem mã nguồn Java. –

1

Tôi đã gần đây đã cố gắng để khám phá hiệu suất IO. Từ những gì tôi đã quan sát, trực tiếp viết thư cho một số FileOutputStream đã dẫn đến kết quả tốt hơn; mà tôi đã gán cho cuộc gọi bản địa của FileOutputStream cho write(byte[], int, int). Hơn nữa, tôi cũng đã quan sát thấy rằng khi độ trễ của BufferedOutputStream bắt đầu hội tụ theo hướng trực tiếp FileOutputStream, nó dao động nhiều hơn tức là nó đột ngột có thể tăng gấp đôi (tôi chưa thể tìm ra lý do).

P.S. Tôi đang sử dụng Java 8 và sẽ không thể nhận xét ngay bây giờ về việc liệu các quan sát của tôi có giữ các phiên bản java trước đó hay không.

Dưới đây là đoạn code tôi thử nghiệm, nơi đầu vào của tôi là một tập tin ~ 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"))); 
     } 
    } 
} 
} 

OUTPUT:

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] 
+0

Tôi chạy thử nghiệm tương tự và tôi có thể xác nhận rằng bằng cách sử dụng 'BufferedOutputStream' làm cho việc ghi tệp không nhanh hơn nhưng chậm hơn, rất có thể vì dữ liệu đang được ghi đã được lưu trữ ở nhiều cấp độ trên đường từ JVM đến OS Trung bình. –

+0

@GOTO Cảm ơn bạn đã xác nhận. Có bất kỳ tài nguyên nào mà bạn có thể biết, điều đó có thể giúp tôi tìm hiểu sâu hơn về cách IO và bộ nhớ cache nội bộ hoạt động không? –

+0

Không thực sự. Nếu nó giúp googling, các thành phần bộ nhớ đệm tập tin được gọi là Cache Manager trong Windows và Page Cache trong Linux. Đĩa cứng và các thiết bị lưu trữ khác cũng đi kèm với các loại lưu trữ I/O khác nhau (mặc dù cơ bản có thể giống nhau). –

Các vấn đề liên quan