【译】太“抓马”了,打印 "B" 要比打印 "#" 慢得多?

100 阅读2分钟

原文:Why is printing "B" dramatically slower than printing "#"?

by:T.J. CrowdercнŝdkBob Kerns

问题:

cнŝdk

我创建了两个 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("");
}

为什么这么“抓马”(dramatically),两次打印矩阵的时间差距这么大?


回答:

T.J. Crowder

纯猜测:你正用一个终端打印矩阵,这个终端尝试识别单词以自适应换行(word-wrapping)而非识别字符(character-wrapping),并将字符 B 识别为某个单词的字符,将字符 # 视为非单词字符。因此,当到达某一行的行尾,终端会搜索一处来断开这行,当它看到字符 #,会愉快地从那里断开;而对于字符 B,终端需要更长的时间搜索,并且可能有更多的文本需要包装(这一操作在一些终端上可能是很耗时的,例如,终端需要退格,然后输出空格覆盖那些被换行的字符)。

但以上只是推测哈。

补充

T.J. Crowder

System.out.println 本身并不会自动换行;但它的输出对象(终端)正在进行自动换行(和阻塞,所以 System.out.println 必须等待)。

Bob Kerns

精彩的推理。我们应该从这一课中总结出:在测试性能时,应该不使用输出,或应该将输出定向到 /dev/null(Windows 上为 NUL),或者至少将输出定向到一个文件。即使不像这个问题这么令人困惑,在任何类型的控制台(Console)上输出都会有非常昂贵的 IO 消耗,并且总会扭曲时序。