这是所有编程语言的共同点吗?进行多次打印然后使用println似乎更快但将所有内容移动到字符串并且只是打印似乎最快。为什么呢?
编辑:例如,Java可以在不到一秒的时间内找到所有素数高达100万 - 但是打印然后在他们自己的println上全部打印可能需要几分钟!打印时间高达100亿小时!
EX:
package sieveoferatosthenes;
public class Main {
public static void main(String[] args) {
int upTo = 10000000;
boolean primes[] = new boolean[upTo];
for( int b = 0; b < upTo; b++ ){
primes[b] = true;
}
primes[0] = false;
primes[1] = false;
int testing = 1;
while( testing <= Math.sqrt(upTo)){
testing ++;
int testingWith = testing;
if( primes[testing] ){
while( testingWith < upTo ){
testingWith = testingWith + testing;
if ( testingWith >= upTo){
}
else{
primes[testingWith] = false;
}
}
}
}
for( int b = 2; b < upTo; b++){
if( primes[b] ){
System.out.println( b );
}
}
}
}
答案 0 :(得分:24)
println
并不慢,它是由托管操作系统提供的与控制台连接的基础PrintStream
。
您可以自己检查:将大型文本文件转储到控制台,并将相同的文本文件传输到另一个文件中:
cat largeTextFile.txt
cat largeTextFile.txt > temp.txt
读取和写入类似于文件大小(O(n)),唯一的区别是目标不同(控制台与文件比较)。这与System.out
基本相同。
底层操作系统操作(在控制台窗口上显示字符)很慢,因为
答案 1 :(得分:5)
System.out
是一个静态PrintStream
类。 PrintStream
除其他外,还有您可能非常熟悉的方法,例如print()
和println()
等。
输入和输出操作需要很长时间才是Java的独特之处。 “长。”打印或写入PrintStream
只需要几分之一秒,但是这种打印的超过100亿个实例可以累积很多!
这就是为什么你的“将所有内容移动到字符串”是最快的。您构建了巨大的String,但只打印一次。当然,这是一个巨大的打印,但你花时间实际打印,而不是与print()
或println()
相关的开销。
正如Dvd Prd所提到的,字符串是不可改变的。这意味着无论何时将新String分配给旧String但重用引用,实际上都会销毁对旧String的引用并创建对新String的引用。因此,通过使用可变的StringBuilder类,您可以使整个操作更快。这将减少与构建您最终打印的字符串相关的开销。
答案 2 :(得分:4)
我认为这是因为buffering。引文来自文章:
缓冲问题的另一个方面 文本输出到终端窗口。通过 默认情况下,System.out(一个PrintStream)是 line buffered,意思是输出 换行时刷新缓冲区 遇到了字符。这是 对交互性很重要,在哪里 你想要一个输入提示 在实际输入之前显示 输入
解释来自维基百科的缓冲区:
在计算机科学中,缓冲是一种 暂时使用的记忆区域 在移动数据时保持数据 一个地方到另一个。通常, 数据按原样存储在缓冲区中 从输入设备中检索(例如 作为鼠标)或在发送之前 到输出设备(如扬声器)
public void println()
通过写入终止当前行 行分隔符字符串。这条线 分隔符字符串由。定义 系统属性line.separator,是 不一定是一个换行符 字符('\ n')。
当你执行println
时,缓冲区被刷新,这意味着必须分配新内存等,这会使打印变慢。您指定的其他方法需要较少的缓冲区刷新,因此更快。
答案 3 :(得分:3)
查看我的System.out.println replacement。
默认情况下,System.out.print()只是行缓冲,并且与Unicode处理有很多相关的工作。由于其缓冲区大小较小,System.out.println()不适合在批处理模式下处理许多重复输出。每条线都立即冲洗。如果您的输出主要是基于ASCII的,那么通过删除与Unicode相关的活动,总体执行时间会更好。
答案 4 :(得分:1)
你遇到的问题是显示在屏幕上是非常昂贵的,特别是如果你有一个图形窗口/ X-windows环境(而不是纯文本终端)只是用字体渲染一个数字远比你正在做的计算。当您将数据发送到屏幕的速度超过显示数据时,它会缓冲数据并快速阻止。即使写入文件与计算相比也很重要,但它比屏幕上显示的速度快10倍到100倍。
BTW:math.sqrt()非常昂贵,并且使用循环比使用模数(即%)来确定数字是否是倍数要慢得多。 BitSet的效率比boolean [] 高8倍如果我将输出转储到文件中,它很快,但写入控制台很慢,如果我写入控制台,写入文件的数据需要大约相同的时间。
Took 289 ms to examine 10,000,000 numbers.
Took 149 ms to toString primes up to 10,000,000.
Took 306 ms to write to a file primes up to 10,000,000.
Took 61,082 ms to write to a System.out primes up to 10,000,000.
time cat primes.txt
real 1m24.916s
user 0m3.619s
sys 0m12.058s
代码
int upTo = 10*1000*1000;
long start = System.nanoTime();
BitSet nonprimes = new BitSet(upTo);
for (int t = 2; t * t < upTo; t++) {
if (nonprimes.get(t)) continue;
for (int i = 2 * t; i <= upTo; i += t)
nonprimes.set(i);
}
PrintWriter report = new PrintWriter("report.txt");
long time = System.nanoTime() - start;
report.printf("Took %,d ms to examine %,d numbers.%n", time / 1000 / 1000, upTo);
long start2 = System.nanoTime();
for (int i = 2; i < upTo; i++) {
if (!nonprimes.get(i))
Integer.toString(i);
}
long time2 = System.nanoTime() - start2;
report.printf("Took %,d ms to toString primes up to %,d.%n", time2 / 1000 / 1000, upTo);
long start3 = System.nanoTime();
PrintWriter pw = new PrintWriter(new BufferedOutputStream(new FileOutputStream("primes.txt"), 64*1024));
for (int i = 2; i < upTo; i++) {
if (!nonprimes.get(i))
pw.println(i);
}
pw.close();
long time3 = System.nanoTime() - start3;
report.printf("Took %,d ms to write to a file primes up to %,d.%n", time3 / 1000 / 1000, upTo);
long start4 = System.nanoTime();
for (int i = 2; i < upTo; i++) {
if (!nonprimes.get(i))
System.out.println(i);
}
long time4 = System.nanoTime() - start4;
report.printf("Took %,d ms to write to a System.out primes up to %,d.%n", time4 / 1000 / 1000, upTo);
report.close();
答案 5 :(得分:1)
如果您要打印到控制台窗口而不是文件,那将是杀手。
每个角色都必须被绘制,并且在每一行上都必须滚动整个窗口。 如果窗口部分与其他窗口重叠,则还必须进行裁剪。
这比你的计划所做的周期要多得多。
通常这不是一个糟糕的代价,因为控制台输出应该是为了您的阅读乐趣:)
答案 6 :(得分:0)
这里的大多数答案都是正确的,但它们没有涵盖最重要的一点:系统调用。这是导致更多开销的操作。
当您的软件需要访问某些硬件资源(例如,您的屏幕)时,它需要询问OS(或虚拟机管理程序)是否可以访问硬件。这花费很多:
这是有关syscall的有趣博客,最后一个致力于syscall和Java
http://arkanis.de/weblog/2017-01-05-measurements-of-system-call-performance-and-overhead http://www.brendangregg.com/blog/2014-05-11/strace-wow-much-syscall.html https://blog.packagecloud.io/eng/2017/03/14/using-strace-to-understand-java-performance-improvement/