Почему Okio более эффективен, чем BufferedInputStream и BufferedOutputStream?

Я попытался выяснить, почему OkHttp использует Okio, а не BufferedInputStream и BufferedOutputStream для буферизации данных. Я использовал следующий код для проверки:

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

Я вызвал bufferedIO() 5 раз, результаты таковы:

357ms
299ms
311ms
324ms
331ms

Я вызвал okio() 5 раз, результаты такие:

524ms
661ms
555ms
525ms
573ms

По результатам BufferedInputStream и BufferedOutputStream эффективнее Okio. Что-то не так с моей проверкой?


person dreamer    schedule 04.07.2017    source источник


Ответы (2)


Я запустил этот тест на своем компьютере, и мои результаты были очень противоречивыми. Я думаю, что тест в конечном итоге измеряет производительность файловой системы больше, чем библиотека ввода-вывода.

Я убрал дополнительную косвенность в тесте Okio, начав с Source, а не с промежуточного FileInputStream. Я также удалил постраничный цикл, который не нужен для Okio: вы можете просто вызвать writeAll(), чтобы скопировать весь исходный код в приемник:

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

Мои результаты были крайне противоречивы из-за производительности файловой системы — отдельные прогоны различались более чем на 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

В целом Okio был здесь более эффективным, но, возможно, это просто удача. Лучший тест изолировал бы ненадежный ввод-вывод файловой системы. Если вы хотите попробовать это, я заинтересован в результатах!

person Jesse Wilson    schedule 04.07.2017

Я изменил ваш код с

Source bufferSource = Okio.buffer(Okio.source(inputStream));

to

Source bufferSource = null;
try {
      bufferSource = Okio.buffer(Okio.source(targetFile));
} catch (Exception e) {
      e.printStackTrace();
}

Это выглядит намного быстрее, чем раньше, но я не уверен, почему. Нужно время, чтобы проверить исходный код okio.

person Dean Wang    schedule 14.09.2017