2010-12-14 7 views
9

Это что-то общее для всех языков программирования? Выполнение нескольких распечаток, за которыми следует println, кажется более быстрым, но перемещение всего в строку и просто печать, которая кажется самой быстрой. Зачем?Почему System.out.println так медленно?

EDIT: Например, Java может найти все простые числа до 1 миллиона меньше, чем за секунду, но при печати каждый из них на свой собственный println может занять несколько минут! До 10 миллиардов часов для печати!

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

Уход за разъяснением? Я всегда находил println довольно быстрым ... – froadie

+0

@DasWood «Кажется»? Представьте несколько тестов (время + код). – NPE

+0

Он имеет тенденцию быть быстрым на * nix, медленно на окнах. Другими словами, именно эта консольная реализация этих ОС является фактором. – skaffman

ответ

19

println не медленно, это лежащую в основе PrintStream, что связано с консолью, предоставленный хостинг операционной системы.

Вы можете проверить это самостоятельно: сравнить демпинг большой текстовый файл на консоль с конвейеру же текстовый файл в другой файл:

cat largeTextFile.txt 
cat largeTextFile.txt > temp.txt 

Чтение и запись похож и пропорционально размеру файла (O (n)), единственное отличие состоит в том, что назначение отличается (консоль по сравнению с файлом). И это в основном то же самое с System.out.


Основная операция OS (для отображения символов в окне консоли) медленно, потому что

  1. байты должны быть отправлены в консольное приложение (должно быть достаточно быстро)
  2. Каждый символ имеет (обычно) шрифт истинного типа (это довольно медленно, выключение сглаживания может улучшить производительность, кстати)
  3. Отображаемая область, возможно, придется прокручивать, чтобы добавить новую строку в видимую область (лучше всего case: передача бит-бит опера в худшем случае: повторная реновация всей текстовой области)
+0

Хорошо, тогда почему основные операции io настолько медленны? – 2010-12-14 09:46:56

+0

Теоретически это возможно оптимизировать в случае выхода консоли? Например. для вывода буфера для отображения до «одной страницы», поэтому прокрутка должна выполняться только один раз? Я знаю, что ответственность за буферизацию вывода фактически лежит в реализации консоли, просто любопытно. –

2

Я считаю, что это из-за buffering. Цитата из статьи:

Другой аспект буферизации относится к выводу в окно терминала. По умолчанию , System.out (PrintStream) - строка буферизирована, что означает, что буфер вывода сбрасывается, когда встречается символ новой строки . Это важно для интерактивности, где вы хотите получить приглашение ввода , отображаемое перед фактическим вводом любого ввода.

Котировка объяснения буферов из википедии:

В информатике буфер является область памяти используется для временного хранения данных, в в то время как он перемещается из одного места в другое.Как правило, данные хранятся в буфере, как это извлекается из устройства ввода (такого как мышь), либо непосредственно перед отправкой на выходное устройство (такие как громкоговорители)

public void println() 

Завершить текущую строку, написав строку разделителя строк. Строка разделителя строки определяется системным свойством системного свойства и равна необязательно одна символа новой строки символ ('\ n').

Так что буфера get покраснел, когда вы делаете println, что означает, что необходимо выделить новую память и т. Д., Что делает печать медленнее. Другие методы, которые вы указали, требуют меньшего сброса буферов, таким образом, быстрее.

+0

Буферизация в файл или консоль одинакова (они оба являются файловыми дескрипторами внизу), однако файл достигает 300 раз быстрее, что означает, что стоимость буферизации - довольно небольшая часть. –

+0

@Peter Я думаю, вы правы в этом. У диска намного большая пропускная способность по сравнению с экраном? – Alfred

4

System.out является статическим классом PrintStream. PrintStream имеет, помимо прочего, те методы, с которыми вы, вероятно, хорошо знакомы, например print() и println() и другие.

Это не уникально для Java, что операции ввода и вывода занимают много времени. "длинный." печать или запись на PrintStream занимает часть секунды, но более 10 миллиардов экземпляров этой печати могут составить довольно много!

Именно поэтому ваше «перемещение всего в строку» является самым быстрым. Ваша огромная строка построена, но вы печатаете ее только . Конечно, это огромная печать, но вы тратите время на печать, а не на служебные данные, связанные с print() или println().

Как упоминал Dvd Prd, строки неизменяемы. Это означает, что всякий раз, когда вы назначаете новую String старой, но повторно используете ссылки, вы фактически уничтожаете ссылку на старую строку и создаете ссылку на новую. Таким образом, вы можете сделать эту операцию еще быстрее, используя класс StringBuilder, который является изменяемым. Это уменьшит накладные расходы, связанные с созданием этой строки, которую вы в конечном итоге напечатаете.

+0

Между вашим ответом и ответом Альфреда очень полно. Благодарю. – 2010-12-14 09:58:18

+1

Создание строк - не проблема. Если вы просто создаете строки, не печатайте их, они будут работать с одинаковой скоростью. –

+0

См. Мой ответ, который показывает, что для toString требуется очень мало времени или записать файл, на много порядков дольше писать на консоль (даже при простом отображении файла со значениями) –

1

Проблема заключается в том, что отображение на экране очень удобно, особенно если у вас есть графическая среда windows/X-windows (а не чистый текстовый терминал). Просто сделать одну цифру в шрифте намного дороже чем расчеты, которые вы делаете. Когда вы отправляете данные на экран быстрее, чем можете его отображать, они буферизуют данные и быстро блокируют. Даже запись в файл значительна по сравнению с вычислениями, но в 10 раз - 100 раз быстрее, чем отображение на экране.

BTW: math.sqrt() является очень дорогостоящим, и использование цикла происходит намного медленнее, чем использование модуля, т. Е.%, Чтобы определить, является ли число кратным. BitSet может быть в 8 раз более эффективным, чем boolean []

Если я выгружаю вывод в файл, это быстро, но запись на консоль выполняется медленно, и если я пишу на консоль данные, которые были записаны в файл это занимает примерно такое же количество времени.

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(); 
1

Если вы печатаете в окно консоли, а не в файл, который будет убийцей.

Каждый персонаж должен быть окрашен, и на каждой строке нужно прокручивать все окно. Если окно частично наложено на другие окна, оно также должно выполнять обрезку.

Это займет гораздо больше циклов, чем то, что делает ваша программа.

Обычно это не плохая цена, чтобы заплатить, так как вывод на консоль должна быть для вашего удовольствия чтения :)

2

Взгляните на мою System.out.println replacement.

По умолчанию System.out.print() является только строковым буфером и выполняет большую работу, связанную с обработкой Unicode. Из-за небольшого размера буфера System.out.println() не очень хорошо подходит для обработки множества повторяющихся выходов в пакетном режиме. Каждая строка сразу же очищается. Если ваш вывод в основном основан на ASCII, то, удалив связанные с Unicode действия, общее время выполнения будет лучше.

Смежные вопросы