通过Java标准输出更快的输出?

时间:2011-09-05 00:17:28

标签: java performance io

在一个在线评判编程竞赛问题中,我需要通过标准输出在1秒内输出多达50,000行(除了读取多达200,000对我使用缓冲区的整数)。我的逻辑似乎是正确的,但我继续拒绝我的提交超过1秒的运行时间。我删除了我的代码逻辑,只输出一个常量字符串,它仍然超过了时间限制。

对于每一行输出,是否有比使用System.out.println(String s)更快的输出方式?

4 个答案:

答案 0 :(得分:7)

我会使用单个System.out.print调用(或者通过基准测试找出最不合理的调用),如下所示:

String str = "line1\nline2\nline3\n ...";
System.out.print(str);

修改

    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < 500000; i++) {
        sb.append(i).append("\n");
    }
    String str = sb.toString();
    long nt = System.nanoTime();
    System.out.print(str);
    nt = System.nanoTime() - nt;
    System.out.print("\nTime(ms): " + (double)nt / 1000000);

sb.toString()不是免费操作。

上面的笔记本需要大约650毫秒(500,000而非请求的50,000)。

Edit2 :还有其他两个技巧,万一填充时间很重要:

  • 构造具有足够容量的StringBuilder
  • 不要追加 对于每一行(下面的代码每次都附加200行,为此 它使用临时sb1);只有每条线都可以相同才有可能 内容。享受。

    long nt = System.nanoTime();
    StringBuilder sb1 = new StringBuilder(400);
    for (int i = 0; i < 200; i++) {
        sb1.append("l").append("\n");
    }
    String strSb1 = sb1.toString();
    
    StringBuilder sb = new StringBuilder(1000000);
    for (int i = 0; i < 2500; i++) {
        sb.append(strSb1);
    }
    
    System.out.print(sb.toString());
    nt = System.nanoTime() - nt;
    System.out.print("\nTime(ms): " + (double)nt / 1000000);
    
在我的情况下,

~500ms。

答案 1 :(得分:4)

如上所述,解决方案是使用StringBuilder构建String,然后打印从StringBuilder的toString()调用返回的String。这可以并且应该由您测试。

答案 2 :(得分:3)

很可能你没有使用足够的缓冲。如果您写入System.out,它将自动刷新每一行,因此在写入之前将几行分组在一起可以添加一些缓冲。更好的方法是使用适当的缓冲区。

使用StringBuffer会增加缓冲区的固有成本。

long start = System.nanoTime();
 StringBuilder sb = new StringBuilder();
for(long i=0;i<50*1000;i++)
  sb.append("Hello World!\n");
System.out.print(sb);
long time = System.nanoTime() - start;
System.err.printf("Took %d ms%n", time/1000000);

打印

Took 30 ms.

但是在Linux上,如果你知道stdout是一个文件,你可以直接写入它。

long start = System.nanoTime();
String processId = ManagementFactory.getRuntimeMXBean().getName().split("@")[0];
FileOutputStream out = new FileOutputStream("/proc/" + processId + "/fd/1");
BufferedOutputStream bos = new BufferedOutputStream(out);
final byte[] str = "Hello World!\n".getBytes();
for (long i = 0; i < 50 * 1000; i++) {
  bos.write(str);
}
bos.close();
long time = System.nanoTime() - start;
System.err.printf("Took %d ms%n", time/1000000);

打印

Took 9 ms.

但是,您必须要小心优化代码的数量,因为您可以打破基准的隐含规则而不接受它。 ;)

答案 3 :(得分:0)

它还取决于底层操作系统。如果我是你,我只会写一个文件,这要快得多。

如果你不能这样做,那么你正在运行你的应用程序。在具有阻塞控制台输出的操作系统上(与非阻塞控制台输出相反),您可以从编写自己的异步记录器中受益。例如,Log4j具有执行此操作的记录器实现。

基本上你将有一个执行者服务,你提交字符串,并让它写入控制台。当该线程正在写入时,您的原始工作线程将继续工作,从而减少由于等待消息刷新而造成的损失。

控制台输出又可以更好地利用,因为当它完成刷新时,新消息已经准备就绪。

这样您就可以提高吞吐量。