Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is Okio more efficient than BufferedInputStream and BufferedOutputStream?

I tried to find out why OkHttp uses Okio but not BufferedInputStream and BufferedOutputStream to buffer data. I used following code to verify:

private String targetPath = Environment.getExternalStorageDirectory()
        + File.separator + "performance.dat";

private InputStream getInputStream() {
    try {
        File targetFile = new File(targetPath);

        if (targetFile.exists()) {
            targetFile.delete();
        }

        targetFile.createNewFile();
        return new FileInputStream("/sdcard/file.zip");
    } catch (IOException e) {
        e.printStackTrace();
    }

    return null;
}

public void bufferedIO(View view) {
    new Thread() {

        @Override
        public void run() {
            InputStream inputStream = getInputStream();

            if (inputStream == null) {
                return;
            }

            long start = System.currentTimeMillis();

            inputStream = new BufferedInputStream(inputStream, 8192);
            File targetFile = new File(targetPath);
            BufferedOutputStream fileOutputStream = null;

            try {
                fileOutputStream = new BufferedOutputStream(new FileOutputStream(targetFile, true), 8192);
                byte[] buffer = new byte[4096];

                int count;
                while ((count = inputStream.read(buffer)) > 0) {
                    fileOutputStream.write(buffer, 0, count);
                }

                fileOutputStream.flush();
                Log.i("performance", "BufferedInputStream and BufferedOutputStream: " + (System.currentTimeMillis() - start) + "ms");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

                if (fileOutputStream != null) {
                    try {
                        fileOutputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }.start();
}

public void okio(View view) {
    new Thread() {

        @Override
        public void run() {
            InputStream inputStream = getInputStream();

            if (inputStream == null) {
                return;
            }

            long start = System.currentTimeMillis();

            File targetFile = new File(targetPath);
            Source bufferSource = Okio.buffer(Okio.source(inputStream));
            BufferedSink bufferSink = null;

            try {
                bufferSink = Okio.buffer(Okio.sink(targetFile));

                while ((bufferSource.read(bufferSink.buffer(), 4096)) != -1) {
                    bufferSink.emitCompleteSegments();
                }
                bufferSink.flush();

                Log.i("performance", "okio: " + (System.currentTimeMillis() - start) + "ms");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    bufferSource.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

                if (bufferSink != null) {
                    try {
                        bufferSink.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }.start();
}

I called bufferedIO() 5 times, the results are:

357ms
299ms
311ms
324ms
331ms

I called okio() 5 times, the results are:

524ms
661ms
555ms
525ms
573ms

According to the results, BufferedInputStream and BufferedOutputStream are more efficient than Okio. Is there anything wrong with my verification?

like image 931
dreamer Avatar asked Jul 04 '17 10:07

dreamer


People also ask

What is okio android?

Okio is a library that complements java.io and java. nio to make it much easier to access, store, and process your data. It started as a component of OkHttp, the capable HTTP client included in Android. It's well-exercised and ready to solve new problems.


1 Answers

I ran this benchmark on my desktop and my results were very inconsistent. I think the benchmark is ultimately measuring filesystem performance more than the I/O library.

I took out the extra indirection in the Okio benchmark, starting with a Source rather than the intermediate FileInputStream. I also removed the page-by-page loop which isn’t necessary for Okio: you can just call writeAll() to copy an entire source to a sink:

public void okio() throws IOException {
  long start = System.currentTimeMillis();

  File targetFile = new File(targetPath);
  targetFile.delete();

  try (BufferedSink sink = Okio.buffer(Okio.sink(targetFile));
      Source bufferSource = Okio.source(new File(sourcePath))) {
    sink.writeAll(bufferSource);
    System.out.println("okio: " + (System.currentTimeMillis() - start) + "ms");
  }
}

My results were wildly inconsistent due to file system performance - individual runs varied by more than 200%.

okio:  67ms   java.io: 106ms
okio:  98ms   java.io: 106ms
okio: 108ms   java.io: 110ms
okio: 121ms   java.io: 113ms
okio: 125ms   java.io: 116ms
okio: 131ms   java.io: 118ms
okio: 143ms   java.io: 143ms
okio: 154ms   java.io: 145ms
okio: 191ms   java.io: 146ms
okio: 217ms   java.io: 239ms

Overall Okio was more efficient here, but that might just be luck. A better benchmark would isolate the unreliable filesystem I/O. If you’d like to try that, I’m interested in the results!

like image 117
Jesse Wilson Avatar answered Oct 26 '22 16:10

Jesse Wilson