【问题标题】:Why is printing "B" dramatically slower than printing "#"?为什么打印“B”比打印“#”慢得多?
【发布时间】:2014-03-23 17:40:13
【问题描述】:

我生成了1000 x 1000的两个矩阵:

第一个矩阵:O#
第二个矩阵:OB

使用以下代码,第一个矩阵需要 8.52 秒才能完成:

Random r = new Random();
for (int i = 0; i < 1000; i++) {
    for (int j = 0; j < 1000; j++) {
        if(r.nextInt(4) == 0) {
            System.out.print("O");
        } else {
            System.out.print("#");
        }
    }

   System.out.println("");
 }

使用此代码,第二个矩阵需要 259.152 秒才能完成:

Random r = new Random();
for (int i = 0; i < 1000; i++) {
    for (int j = 0; j < 1000; j++) {
        if(r.nextInt(4) == 0) {
            System.out.print("O");
        } else {
            System.out.print("B"); //only line changed
        }
    }

    System.out.println("");
}

运行时间显着不同的原因是什么?


按照 cmets 的建议,仅打印 System.out.print("#"); 需要 7.8871 秒,而 System.out.print("B"); 给出 still printing...

正如其他人指出它对他们正常工作一样,我尝试了 Ideone.com,例如,两段代码以相同的速度执行。

测试条件:

  • 我从 Netbeans 7.2 运行了这个测试,输出到它的控制台
  • 我使用System.nanoTime() 进行测量

【问题讨论】:

  • 尝试将 rand.nextInt(4) == 0 更改为 i
  • 两者似乎都在我的机器上运行了相同的时间,大约 4 秒。
  • 如果您建议打印 B 比打印 #.... 为什么不尝试打印所有 B & all # 而不是依赖随机变量 r
  • 根据接受的答案,您显然没有尝试将输出重定向到文件或 /dev/null 来运行它。
  • @fejese,Random() 不是加密 rng,因此不会用完熵池。

标签: java performance loops for-loop system.out


【解决方案1】:

是的,罪魁祸首肯定是自动换行。当我测试你的两个程序时,NetBeans IDE 8.2 给了我以下结果。

  1. 第一个矩阵:O 和 # = 6.03 秒
  2. 第二个矩阵:O 和 B = 50.97 秒

仔细查看您的代码,您在第一个循环结束时使用了换行符。但是您在第二个循环中没有使用任何换行符。因此,您将在第二个循环中打印一个包含 1000 个字符的单词。这会导致自动换行问题。如果我们在 B 之后使用非单词字符“ ”,则编译程序只需 5.35 秒。而如果我们在传递100个值或50个值之后在第二个循环中使用换行符,则只需要8.56秒7.05秒 分别。

Random r = new Random();
for (int i = 0; i < 1000; i++) {
    for (int j = 0; j < 1000; j++) {
        if(r.nextInt(4) == 0) {
            System.out.print("O");
        } else {
            System.out.print("B");
        }
        if(j%100==0){               //Adding a line break in second loop      
            System.out.println();
        }                    
    }
    System.out.println("");                
}

另一个建议是更改 NetBeans IDE 的设置。首先,转到 NetBeans Tools 并单击 Options。之后点击 Editor 并转到 Formatting 标签。然后在Line Wrap选项中选择Anywhere。编译程序所需的时间将减少近 6.24%。

【讨论】:

  • "编译程序所需的时间将减少近 6.24%。"你肯定指的是执行,而不是编译......
【解决方案2】:

纯猜测是您使用的终端尝试执行 word-wrapping 而不是字符换行,并将 B 视为单词字符,但将 # 视为非字符字字符。因此,当它到达一行的末尾并搜索断行的地方时,它几乎立即看到#,并愉快地断行;而使用B,它必须继续搜索更长的时间,并且可能有更多的文本要换行(这在某些终端上可能很昂贵,例如,输出退格,然后输出空格以覆盖被换行的字母)。

但这纯粹是猜测。

【讨论】:

  • 精彩演绎。但是我们应该从这节课中总结出来,并始终在输出被消除、定向到 /dev/null(Windows 上的 NUL)或至少到一个文件的情况下衡量性能。在任何类型的控制台上显示通常都是非常昂贵的 IO,并且总是会扭曲时间——即使没有像这样令人困惑。
  • @BobKerns:当然,这仍然会产生一个适当的问题。问题仍然是打印 Bs 需要更长的时间并且不打印它们并不是真正的解决方案,而是帮助调试问题的技巧。
  • @MrLister: System.out.println 不做自动换行;它输出的东西是自动换行(和阻塞,所以System.out.println 必须等待)。
  • @Chris -- 实际上,我认为不打印它们是解决获得算法准确时间问题的解决方案。每次打印到控制台(任何类型的)时,您都会调用与您正在测试的性能无关的各种外部处理方式。这是您的测量过程中的一个错误,纯粹而简单。另一方面,如果您不将问题视为测量,而是理解差异,那么是的,不打印是一种调试技巧。归根结底,您要解决哪个问题?
  • @BobKerns 感谢您为这个线程注入了一些理智!很多人似乎不明白这一点。您需要小心,您只测量您要测量的东西
【解决方案3】:

我在 Eclipse 和 Netbeans 8.0.2 上进行了测试,两者都使用 Java 版本 1.8; 我使用System.nanoTime() 进行测量。

日食:

在两种情况下都得到了相同的时间 - 大约 1.564 秒

Netbeans:

  • 使用“#”:1.536 秒
  • 使用“B”:44.164 秒

因此,Netbeans 的打印到控制台的性能似乎很差。

经过更多研究,我意识到问题出在 Netbeans 的最大缓冲区的line-wrapping(它不限于System.out.println 命令),如下代码所示:

for (int i = 0; i < 1000; i++) {
    long t1 = System.nanoTime();
    System.out.print("BBB......BBB"); \\<-contain 1000 "B"
    long t2 = System.nanoTime();
    System.out.println(t2-t1);
    System.out.println("");
}

每次迭代的时间结果小于 1 毫秒,除了每五次迭代,当时间结果约为 225 毫秒时。类似于(以纳秒为单位):

BBB...31744
BBB...31744
BBB...31744
BBB...31744
BBB...226365807
BBB...31744
BBB...31744
BBB...31744
BBB...31744
BBB...226365807
.
.
.

等等..

总结:

  1. Eclipse 与“B”完美配合
  2. Netbeans有一个换行问题可以解决(因为eclipse中不会出现这个问题)(不用在B(“B”)后面加空格)。

【讨论】:

  • 您能否详细说明您的研究策略,最后是什么让您发现换行是罪魁祸首? (我很好奇你的侦探技巧!)
  • 可以禁用 Netbeans 中的自动换行。
  • 我在身边的时间越长,Netbeans 的吸引力就越小。
  • “我使用了 System.nanoTime()” ...一般来说,没有专门构建的基准测试工具的微基准测试毫无意义。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-22
  • 1970-01-01
相关资源
最近更新 更多