在一个在线编程竞赛中,我需要通过标准输出输出多达50,000行的内容,并在1秒内完成(此外还需读取多达200,000个整数对,我使用了缓冲区)。我的逻辑似乎是正确的,但我一直因为超过1秒运行时间而被拒绝提交。我简化了我的代码逻辑,只输出一个常量字符串,但仍然超过了时间限制。
有没有比每行输出使用
System.out.println(String s)
更快的输出方式?
System.out.println(String s)
更快的输出方式?
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()
不是一个免费的操作。
在我的笔记本上,以上操作需要大约650ms(使用500,000而不是请求的50,000)。
编辑2:如果填充时间很重要,则可以使用其他两个技巧:
don't append
for every line (the code below appends 200 lines every time, for this
it uses a temp sb1
); only possible if every line can have the same
content. Enjoy.
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);
在我的情况下,大约需要500毫秒。
System.out.print
之前分配 String str = stringBuilder.toString()
,以便 toString()
的成本不计入输出所需的时间。 - Marius Burz如上所述,解决方案是使用StringBuilder
类构建您的String
,然后使用StringBuilder.toString()
方法获取结果。
您应该自行测试此功能。
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.
然而,您必须小心地优化代码的数量,因为您可能会打破基准测试的隐含规则并且无法得到批准。 ;)
stdout
确实有一定的优点,但在这种情况下为什么要使用Java呢?不如选择C
,而不是使用依赖于平台的Java。 - Marius BurzStringBuilder
,它类似于缓冲,但更明显的选择可能是BufferedOutputStream
。 - Peter Lawrey这也取决于底层操作系统。如果我是你,我会直接写入文件,这样速度更快。
如果你不能这样做,并且你的应用程序在具有阻塞控制台输出(而不是非阻塞控制台输出)的操作系统上运行,那么你可以从编写自己的异步记录器中受益。例如,Log4j有实现此功能的记录器。
基本上,您将拥有一个执行器服务,您可以向其中提交字符串,让它写入控制台。当该线程正在写入时,您的原始工作线程将继续工作,从而减少等待消息刷新的损失。
控制台输出也得到更好的利用,因为在完成刷新时,新消息已经准备好了。
这样,您可能能够增加吞吐量。