2

第一段代码:

// code is a private "global variable" for the class
// SourceCodeBuilder is a class that uses StringBuilder()
// basically it is based on String(s), formatted and with many appends depending on the "loc()" calls (see below)
private SourceCodeBuilder code = new SourceCodeBuilder();

[...]

    // create "file.txt" and call algorithm
    fileOut = new FileWriter("file.txt");

    for (int i=0; i<x; i++) {
        algorithm();
    }

其中algorithm()是这样的方法:

private void algorithm () {
    for (int i=0; i<y; i++) {
        code.loc("some text");
        code.loc("other text");
        ...
    }

    // after "building" the code value I wrote it on the file
    fileOut.write(code.toString());
    fileOut.flush();
    code.free(); // this call "empties" the code variable (so the next time algorithm() is called it has the code var sets to "" - it frees a lot of memory)
                 // basically it calls "setLength(0)" method of StringBuilder
}

当我在大型文本文件上执行所有这些操作时,执行大约需要 4500 毫秒,并且内存不到 60MB。

然后我尝试使用其他代码。第二段代码:

private SourceCodeBuilder code = new SourceCodeBuilder();

[...]

    // create "file.txt" and call algorithm
    fileOut = new FileWriter("file.txt");

    for (int i=0; i<x; i++) {
        algorithm();
    }

    fileOut.write(code.toString());
    fileOut.flush();
    fileOut.close();

这个 time algorithm()是这样的方法:

private void algorithm () {
    for (int i=0; i<y; i++) {
        code.loc("some text");
        code.loc("other text");
        ...
    }
}

它需要超过 250MB 的内存(这没关系,因为我没有在代码变量上调用“free()”方法,所以它是对同一变量的“连续”附加),但令人惊讶的是它需要超过 5300 毫秒执行。这比第一个代码慢了大约 16%,我无法向自己解释原因。

在第一个代码中,我在“file.txt”上多次写入小段文本。在第二个代码中,我在“file.txt”上写了一大段文本,但只写了一次,并且使用了更多内存。对于第二个代码,我期望更多的内存消耗,但甚至不会消耗更多的 CPU(只是因为有更多的 I/O 操作)。

结论:第一段代码比第二段代码快,即使第一段代码比第二段代码执行更多的 I/O 操作。为什么?我错过了什么吗?

4

2 回答 2

4

每个系统调用都有开销,您可以通过使用 BufferedWriter 或读取器或流来避免。(这就是为什么你会使用缓冲)

在第一种情况下,您在写入之前缓冲整个内容。在第二种情况下,您一次写入一点文件,这将导致更多的系统调用,从而导致更多的开销。

如果您要更快地生成文件,您可能会发现几乎所有时间都花在了系统调用上。

您在块中流式传输数据(使用缓冲)的原因是您不使用太多内存。即有一个点,较大的缓冲区会减慢您的速度而不是帮助您。

在您的情况下,我怀疑您正在写入 StringBuilder 或 StringWriter(使用 StringBuffer),并且必须复制它,因为它被调整为您最终需要的大小。这会产生一些 GC 开销,从而导致更多的复制。

于 2012-06-28T14:38:23.260 回答
3

当您慢慢填充大内存缓冲区时,所需的时间会非线性增长,因为您需要多次重新分配缓冲区,每次都将整个内容复制到内存中的新位置。这需要时间,尤其是当缓冲区为 200MB+ 时。如果您预先分配缓冲区,您的过程可能会更快。

然而,以上所有只是我的猜测。您应该分析您的应用程序以查看额外时间的真正用途。

于 2012-06-28T14:38:36.183 回答