【问题标题】:Why is Okio more efficient than BufferedInputStream and BufferedOutputStream?为什么 Okio 比 BufferedInputStream 和 BufferedOutputStream 更高效?
【发布时间】:2017-12-07 19:33:51
【问题描述】:

我试图找出为什么 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 效率更高。我的验证有什么问题吗?

【问题讨论】:

    标签: android okhttp okio


    【解决方案1】:

    我在我的桌面上运行了这个基准测试,结果非常不一致。我认为基准测试最终衡量的是文件系统性能,而不是 I/O 库。

    我在 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 在这里效率更高,但这可能只是运气。更好的基准测试将隔离不可靠的文件系统 I/O。如果你想试试,我对结果很感兴趣!

    【讨论】:

      【解决方案2】:

      我更改了您的代码

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

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

      它看起来比以前快得多,但我不知道为什么。 okio源码需要时间查看。

      【讨论】:

        猜你喜欢
        • 2017-04-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-28
        • 1970-01-01
        • 1970-01-01
        • 2021-05-07
        相关资源
        最近更新 更多