Вероятная причина заключается в реализации PrintStream
, для которого System.out
установлено (по умолчанию).
В Java 11 метод, создающий PrintStream
, имеет вид следующим образом:
private static PrintStream newPrintStream(FileOutputStream fos, String enc) {
if (enc != null) {
try {
return new PrintStream(new BufferedOutputStream(fos, 128), true, enc);
} catch (UnsupportedEncodingException uee) {}
}
return new PrintStream(new BufferedOutputStream(fos, 128), true);
}
Ключевая строка такова:
return new PrintStream(new BufferedOutputStream(fos, 128), true, enc);
Во-первых, создается BufferedOutputStream
с размером буфера 128 байтов. Это мало. Если вы выполняете небольшую запись в System.out
, будет буфер flu sh , по крайней мере, каждые 128 байт.
Во-вторых, параметр true
включает автоматическую очистку. Javado c описывает это следующим образом:
"Если true, выходной буфер будет очищаться всякий раз, когда записывается байтовый массив, вызывается один из методов println
или символ новой строки или байт ('\n'
) пишется "
Опять же, это означает, что есть больше смыва.
Почему это имеет значение?
Очистка скважины включает в себя выполнение системного вызова fwrite
для записи символов. Системные вызовы относительно дороги. Согласно некоторым числам, которые я видел, издержки системного вызова fwrite
системного вызова составляют порядка от 60 до 350 нс. Это не так уж и много, но если вы сделаете это несколько раз за l oop итераций по сравнению с одним разом за l oop итераций, и повторяете это достаточно долго, разница может быть значительной.
Могут также быть издержки, которые зависят от того, к чему подключен System.out
. Например, если вы выполняете запись в консоль, большое количество мелких записей может замедлить работу консольного приложения.
Возможно другое объяснение. Код теста, который вы показали, не учитывает возможные эффекты разогрева JVM. Например, может быть, что один пример запускает JIT-компиляцию, а другой нет. Издержки компиляции JIT могут привести к тому, что первое займет больше времени, чем второе.