【问题标题】:How can we enhance performance using hooking?我们如何使用挂钩来提高性能?
【发布时间】:2014-04-09 03:36:46
【问题描述】:

Java 文档说Java Docs

公共类 BufferedReader 扩展阅读器

从字符输入流中读取文本,缓冲字符以提供>高效读取字符、数组和行。

可以指定缓冲区大小,也可以使用默认大小。默认值>对于大多数用途来说足够了。

通常,由 Reader 发出的每个读取请求都会导致相应的读取请求由底层字符或字节流组成。因此,建议将 >BufferedReader 包裹在任何其 read() 操作可能成本高昂的 Reader 周围,例如 >FileReaders 和 InputStreamReaders。例如,

BufferedReader 中 = new BufferedReader(new FileReader("foo.in"));

将缓冲来自指定文件的输入。如果没有缓冲,每次调用 >read() 或 readLine() 都可能导致从文件中读取字节,转换为 >characters,然后返回,这可能非常低效。

但是通过挂钩 Filereader,我们使用 FileReader 的 read 方法来读取一次读取一个字符的文件,所以如果我的文件包含 2000 个字符,FileReader 首先将一次读取 2000 个字符并将其传输到缓冲区,我们将读取从缓冲区使用 bufferedreader 那么它如何提高性能。我们只能使用 FileReader 来做到这一点?

【问题讨论】:

  • 必须一次“阅读”一个字符。 FileReader 将一个或多个字节的序列变成可行的字符;根据所使用的编码,一个字符可能被编码为几个字节。特别是在 UTF-8 的情况下,这可以少至 1 个字节或多至 3 个字节。 BufferedReader 只是通过缓冲解码操作的结果来减少从原始文件读取所需的次数。
  • @fge 所以你说在这种情况下 Filereader 将一次读取所有字符。
  • 不,只有块;想象一个文件有几个 GiB 大......
  • @fge 它如何读取大块它没有 readline() 方法去下一行
  • BufferedReader 将控制它;或者,事实上,任何Reader。查看Reader 的javadoc:它具有用字符填充char[] 的方法。

标签: java bufferedreader filereader


【解决方案1】:

你可以这样想象:

您位于A 位置,您的数据位于B 位置,其中AB 相距5 公里。

通过一次检索一个单元的数据,您必须每次从AB 进行旅行(读取基础字符或字节流的请求),因此如果您有 100 个数据单元你要去旅行 100 次,这太费时间了。

当缓冲时,您就像有一辆卡车,当您旅行时,您将一些数据单元(缓冲区的大小)加载到您的装载地板(缓冲区)上,这样您就不必旅行了很多次,因此很有效率。

通常在您打开一个文件进行阅读时,您希望阅读更多的字符,因此首选 BufferedRead。

写作也是如此。

编辑

首先,FileReader 不会读取自身,它是底层输入流。在这种情况下FileInputStream

BufferedReader为你管理FileReasder,并在其fill()方法中使用方法

abstract public int read(char cbuf[], int off, int len) throws IOException;

你的Reader 来填充缓冲区。 它不使用只读取单个字符的方法! 换句话说,当填充方法必须进行旅行时,它会从底层检索一系列字符输入流并将其保存在内存中,这样当您需要读取时,它首先会在缓冲区中查找,使用它并在必要时重新填充它。

您可以通过自己调用FileReader 中的read(char cbuf[], int off, int len) 来实现相同的目的,但是您将拥有的只是一系列字符,例如以下文本

Lorem ipsum dolor sit amet, consetetur sadipscing elitr,
sed diam nonumy eirmod tempor invidunt ut labore et dolore
magna aliquyam erat, sed diam voluptua.

例如可能看起来像这样

"onsetetur sadipscing elitr,\r\nsed diam nonumy eirmod temp"

如果需要,您将不得不处理它以找到一行文本。

但你不必这样做,因为BufferedReader 会为你处理这件事,这也是你最好写的原因之一

BufferedReader in = new BufferedReader(new FileReader("foo.in"));

【讨论】:

  • 所以你说在这种情况下 Filereader 会一次读取所有字符
  • @TruePS,不,我回答的哪一部分让你这么想?
  • 实际上你很好地向我解释了缓冲的概念,但我想知道为什么在 Bufferedreader 的构造函数中挂钩 Filereader 的实例是必要的,所以当我们挂钩并调用 BufferedReader 的 readline() 方法时,它会在内部调用Filereader 的 read 方法,如果发生这种情况,我们为什么需要缓冲阅读器,因为 filereader 的 read 方法一次读取 1 个字符,浪费我们的时间,忘记缓冲概念现在告诉我为什么我们写 >BufferedReader in = new BufferedReader(new FileReader ("foo.in"));
  • @A4LI 认为你错了我可以使用 read(char cbuf[], int off, int len) 管理 Filereader 读取的行。见public class Reader { public static void main(String[] args) throws IOException { try (FileReader in = new FileReader("D:/test.txt")) { char[] cbuf = new char[1000]; int line = in.read(cbuf, 1, 100); while(line!=-1) { System.out.print(cbuf); line = in.read(); } } } }
  • @TruePS 这是short tutorial for the netbeans debugger,它涵盖了基本步骤。
【解决方案2】:

BufferedReader 是使用缓冲区的常见示例。

我们分解BufferedReader(J7u51)的read()方法:

ensureOpen();
for (;;) {
    if (nextChar >= nChars) {
        fill();
        if (nextChar >= nChars)
            return -1;
    }

fill() 方法(调用底层读取器)当且仅当我们请求的字符数超过之前调用的字符数时才会被调用。

    if (skipLF) {
        skipLF = false;
        if (cb[nextChar] == '\n') {
            nextChar++;
            continue;
        }
    }
    return cb[nextChar++];
}

这里我们只返回适当的值。

所以 BufferedReader 总是“读得更多,以防你以后想要更多”,然后,如果你真的想要更多,可以有效地给你“更多”。

看看这个例子:

YourApp                   BufferedReader (bufferSize=10)        FileReader
read(1 character)   ->
                          buffer empty, load 10 characters  ->  disk latency 10 ms
                                                            <-  return 10 characters
                          buffer filled with 10 characters
                    <-    return 1 character, 9 remaining 
                          in buffer
read(1 character)   ->    
                    <-    return 1 character, 8 remaining 
                          in buffer
.
.
.
read(1 character)   ->    
                    <-    return 1 character, 0 remaining 
                          in buffer

循环的总执行时间为 10 毫秒 + 微不足道的 Java 开销。

现在,将其与无缓冲版本进行比较:

YourApp                   FileReader

read(1 character)   ->
                          disk latency 10 ms
                    <-    return 1 character
read(1 character)   ->    
                          disk latency 10 ms
                    <-    return 1 character
.
.
.
read(1 character)   ->    
                          disk latency 10 ms
                    <-    return 1 character

HDD 调用造成的开销比前一种情况大得多。

【讨论】:

  • 我想我明白了,我只是想在最后一张图片中问几件事 Filereader 返回 10 个字符,那么它为什么会在接下来的两个中返回 1 个字符(根据我的 char 数组)如果 10 个字符存储在 char 数组中,则调用,那么为什么每次磁盘延迟都是 10 毫秒。第二个问题是,如果我们在 Bufferedreader 的构造函数中挂钩 Filereader,那么当我们调用 readline() 方法时,它是在内部调用 filereader 的读取方法吗?
【解决方案3】:

首先,BufferedReader 一次不会完全读取包装好的Reader。相反,如果需要,它会读取特定大小的块(默认为 8192 个字符)。

其次,使用BufferedReader 而不是直接使用FileReader 的原因是访问文件比访问内存中的对象复杂得多。因此,如果您逐行读取文件,您将每行访问一次文件系统。使用BufferedReader,您可以减少读取操作的次数,从而提高代码的性能。

【讨论】:

  • 我无法给出更好的答案。
  • @Robin >首先,BufferedReader 一次不会完全读取包装好的 Reader。相反,如果需要,它会读取特定大小的块(默认 >8192 个字符)。我知道我要问的只是使用 FileReader 将数据转移到缓冲区中浪费的时间,FileReader 一次读取一个字符。
【解决方案4】:

谁说FileReader 实例将用于一次读取一个字符?

你的问题最好的解决办法是直接去BufferedReader的源代码。如果您对此进行研究,您会发现,在内部,BufferedReader 将调用包装的 FileReaderread(char[],int,int) 方法(或者它包装的任何 Reader)。

Here 你有BufferedReaderread 方法的代码。对包装的Reader 的实际调用是在fill 方法中执行的。我认为值得检查一下。

【讨论】:

  • 让我们避免使用 read() 函数,告诉我挂钩的好处我也可以使用 FileReader 读取文件。
  • BufferedReader 使用字符缓冲区,尝试优化来自文件系统的输入。因此,假设您现在阅读了 10 个字符,然后是 8000 个字符,然后是另外 182 个字符(对 read 方法进行了 3 次不同的调用)。如果您使用缓冲区大小为 8192 个字符的包装器,它将导致文件仅被访问一次,即从一开始就抓取所有 8192 个字符。当然,您可以仅使用 FileReader 自己编写此代码,但这就是 Java API 的用途:为您提供高度优化的功能,否则您必须自己编写。
  • 关于您的评论的最后一个问题如果我仅使用 FileReader 读取 8192 个字符,那么性能会如何受到影响,因为通过挂钩您在回答 BufferedReader will call the read(char[],int,int) method of the wrapped FileReader 中进行了解释。如果我正在使用 Filereader 读取文件并且调用 Filereader 的 read(char[],int,int) method 我将在不使用缓冲区的情况下获得相同的输出,因此由于缓冲,我将节省开销。
猜你喜欢
  • 2012-08-23
  • 1970-01-01
  • 2018-09-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-11
相关资源
最近更新 更多