Java IO性能调优问题

3

我正在尝试优化一个大量写入流的基于Web的应用程序。 代码类似于这样 ---

 StringWriter stringWriter = new StringWriter(1024);
 PrintWriter printWriter = new PrintWriter(stringWriter);

然后,这个PrintWriter被用于在多个地方进行写操作,例如--
 printWriter.write("set interface ethernet0 zone Trust");

我希望通过将PrintWriter包装在BufferedWriter中来优化几个写操作。因此,我计划将第1行和第2行更改为以下内容。请让我知道下面的方法是否正确 -

StringWriter stringWriter = new StringWriter(1024);
// go for bufferedwriter instead of printwriter.
BufferedWriter bufWriter = new BufferedWriter(stringWriter, 8*1024); 

此外,我认为现在可以直接替换write方法,例如:--
printWriter.write("set interface ethernet0 zone Trust");

替换为
bufWriter.write("set interface ethernet0 zone Trust");

请告诉我这是否正确,或者我需要使用BufferedWriter的哪些重载方法来充分利用它。谢谢,Dev。

@user,我可以知道您想从上述代码中实现什么吗?请告诉我们您在业务上想要做什么。 - Dead Programmer
我建议您对应用程序进行性能分析。如果它运行缓慢,这很可能不是原因。 - Peter Lawrey
4个回答

11

由于StringWriter本身就是将内容写入内存中的字符串缓冲区,因此将其包装成BufferedWriter不应该对性能产生任何影响。(由于额外一层间接级别,实际上这可能会使您的代码稍微慢一些 - 尽管这个问题可能会被JIT编译器优化掉。)

后者在文件I/O中非常有用,因为一次大的写操作往往比多次小的写操作更快。

您认为您需要调整这段代码的原因是什么?值得注意的是,大多数情况下不需要进行性能优化。以下是一些关于性能调优的通用指南:

性能调优的一般指南

  1. 不要做这件事(您确定您需要吗?大部分情况下实际上并不需要)。
  2. 暂时不要这样做(相关的经典引文:"过早优化是万恶之源"(Donald Knuth))。
  3. 不要优化您认为速度较慢的代码 - 使用性能分析工具来查找实际的瓶颈。
  4. 在任何更改后再次测量性能,以验证您确实显著提高了性能(例如,很容易进行“聪明”的更改,但并不会有任何区别,甚至会恶化性能)。

将数据写入 StringWriter 的成本是复制数据的成本。使用 BufferedWriter 会使这个成本翻倍。我不相信 JIT 能够优化掉这个问题。 - Peter Lawrey
同意每一个词。我也想写类似的东西,但很高兴我没有这样做。因为我表达不了这么好。 - AlexR

4

添加BufferedWriter仅为每个写入的字符增加了额外的复制操作。相反,您应该改变StringWriter的初始大小,较大的初始大小意味着后续生长和复制较少。

StringWriter本身包含一个用于字符数据的增长缓冲区,通过使用BufferedWriter在其上添加另一个缓冲区不会导致加速。从给定的代码中可以看出,增加StringWriter对象本身的初始大小可能有所帮助。


你好,感谢你的建议。我已经使用了性能分析器来分析应用程序的行为。我发现应用程序几乎70%的时间都花在我想要更改的代码区域。 - user496934
基本上,该应用程序是一种转换工具,将一种格式的行转换为另一种格式。对于输入中的每一行,都会调用一个方法,该方法调用printwriter.write()方法,因此应用程序所花费的大部分时间都在这个方法中。出于这些原因,我正在尝试优化此方法,并查看是否可以通过使用BufferedWriter而不是PrintWriter来优化write方法。请相应地提供建议。 - user496934

3
您真的需要使用StringWriter吗?StringWriter只是StringBuffer的代理,每次调用append时都会在自身上进行同步。
您考虑过使用StringBuilder吗?它不是线程安全的(因此不是线程安全的),但应该更快-特别是如果您正在进行大量的短写入。
编辑:同步问题是一个双重重要的问题,因为PrintWriter也是同步的。每次调用print/write时,您的代码都必须获取和释放两个锁定。我怀疑这可能是您的代码在write方法中花费大量时间的原因。
编辑: 如果您确实必须使用PrintWriter,则以下替代方案可能有效,前提是PrintWriter仅由一个线程访问-否则可能会导致死锁。要使其正常工作,所有写入都必须在同一同步块中执行,否则您将遇到相同的问题-例如进入并退出许多同步的代码位。
    ByteArrayOutputStream out = new ByteArrayOutputStream(bufSize);

    PrintWriter writer = new PrintWriter(out);

    synchronized (writer) {
        // do writes
    }

    writer.flush(); // very important or you may not see any output in 'out'
    writer.close();

    String output = out.toString();

如果您不必使用PrintWriter,那么仅使用StringWriter应该就足够了。
    StringWriter writer = new StringWriter(bufSize);

    synchronized(writer.getBuffer()) {
        // do writes
    }

    writer.flush();
    writer.close();

    String output = writer.toString();

我的初步建议是:
    StringBuilder builder = new StringBuilder();

    // do APPENDS (instead of writes)

    String output = builder.toString();

你需要调用append而不是write,这就是为什么你需要重构你的代码。

嗨Dunes,然而我不能将StringBuilder作为StringWriter的替代方案传递,因为PrintWriter构造函数需要一个writer实例作为参数。那么我该如何优化下面的代码呢?我可以探索哪些替代数据结构-- StringWriter stringWriter = new StringWriter(1024); PrintWriter printWriter = new PrintWriter(stringWriter); 然后是多个write语句,例如printWriter.write()。 - user496934
那么在这种情况下使用 BufferedWriter 会解决问题吗?请给予建议。 - user496934
不,BufferedWriter 只会让事情变得更糟(正如其他人所说,并且也解释了原因)。 - Dunes

0
我真的怀疑BufferedWriter在这里会带来任何性能改进 - 它和StringWriter都直接写入内存。如果有什么不同之处,它只会通过添加间接层轻微地减慢速度。当然,如果你发现不同,请纠正我。你认为这是瓶颈的原因是什么?

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接