在一个在线评判编程竞赛问题中,我需要通过标准输出在1秒内输出多达50,000行(除了读取多达200,000对我使用缓冲区的整数)。我的逻辑似乎是正确的,但我继续拒绝我的提交超过1秒的运行时间。我删除了我的代码逻辑,只输出一个常量字符串,它仍然超过了时间限制。
对于每一行输出,是否有比使用System.out.println(String s)
更快的输出方式?
答案 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 :还有其他两个技巧,万一填充时间很重要:
不要追加
对于每一行(下面的代码每次都附加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具有执行此操作的记录器实现。
基本上你将有一个执行者服务,你提交字符串,并让它写入控制台。当该线程正在写入时,您的原始工作线程将继续工作,从而减少由于等待消息刷新而造成的损失。
控制台输出又可以更好地利用,因为当它完成刷新时,新消息已经准备就绪。
这样您就可以提高吞吐量。