Why use BufferedInputStream if InputStream provides read with a buffer

Why did you need to include the BufferedInputStream wrapper in the Java I / O system, if all implementations of the InputStream interface by default contain the read(byte b[]) method, which uses a buffer to read from the stream?

For example, I wrote a program that copies a video file of ~100 MB from one file to another.

  1. Implementation using BufferedInputStream:

    public static void main (String[] args) throws IOException{
    
        BufferedInputStream fis=new BufferedInputStream(new FileInputStream("C:/tests/1.mp4"));
        BufferedOutputStream fos=new BufferedOutputStream(new FileOutputStream("C:/tests/2.mp4"));
    
        int b=fis.read();
        while (b!=-1){
            fos.write(b);
            b=fis.read();
        }
        fos.flush();        
    }
    
  2. Implementation using the read(byte b[]){[9 method]}

    public static void main (String[] args) throws IOException{
    
        FileInputStream fis=new FileInputStream("C:/tests/1.mp4");
        FileOutputStream fos=new FileOutputStream("C:/tests/2.mp4");
    
        byte[] buff=new byte[4096];
        int bytes=fis.read(buff);
    
        while(bytes!=-1){
            fos.write(buff, 0, bytes);
            bytes=fis.read(buff);
        }           
    }
    

Implementation with using the BufferedInputStream class is about 10 times slower than

Author: insolor, 2018-11-27

1 answers

Try comparing the processing speed with buffering and reading the array:

BufferedInputStream fis = new BufferedInputStream(new FileInputStream("C:/tests/1.mp4"));
BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream("C:/tests/2.mp4"));
byte[] buff = new byte[4096];
int bytes = fis.read(buff);
while (bytes != -1) {
    fos.write(buff, 0, bytes);
    bytes = fis.read(buff);
}
fos.flush();

I believe that the processing speed will be comparable to the speed without buffering, and maybe even faster, due to the different buffer size.

Why is the first example slow

Reading / writing an array of bytes from a local file is a very fast operation. In the best case BufferedInputStream (BufferedOutputStream) will not affect performance in a significant way, at worst-performance will drop due to various overhead costs.

In your case, the bytes (~ 10^8) after reading from the buffer are processed one at a time, which greatly increases the overhead:

  • methods read and write are called for each byte - method calls are not free;
  • worse, both methods are synchronized (synchronized) and the VM performs a context switch/lock on each call;
  • in the methods themselves, there are various checks (whether it is open input / output stream, whether the buffer has run out, etc.), which also take time.

I believe that synchronization creates the main load. For the experiment, you can add synchronized methods to the option with FileInputStream

    while (bytes != -1) {
        for (int i = 0; i < bytes; i++) {
            write(read(buff[i]));
        }
        fos.write(buff, 0, bytes);
        bytes = fis.read(buff);
    }
}

static int count = 0;

public static synchronized void write(int b) {
    //неважно
    count += b;
}

public static synchronized int read(int b) {
    return count + b;
}

Why BufferedInputStream

Buffered streams are needed to avoid writing buffering yourself. They also allow you to separate the program logic from the buffering settings. BufferedInputStream is especially useful if:

  1. The algorithm is used, which processes bytes sequentially. In this case, you can simplify the code by entrusting buffering to the built-in class.

  2. The stream itself is used as an argument for the reader class.

    For example, BufferedInputStream can be passed as an argument to Scanner:

    Scanner scanner = new Scanner(new BufferedInputStream(new FileInputStream("input")));
    

    After that, using Scanner , you can work with the data line by line, and BufferedInputStream will save on accessing the file system.

  3. Specific methods are used.

    Methods InputStream.mark and InputStream.reset, typically not available in unbuffered streams. Accordingly, if you need to roll back the state of the stream, you will not be able to use FileInputStream.

Using the example of copying a file, it is difficult to see the advantages. On the other hand, it is better to use built-in methods rather than threads to copy a file.

 6
Author: default locale, 2018-11-27 20:07:27