Почему печать «B» значительно медленнее, чем печать «#»?


2749

Я сгенерировал две матрицы 1000х 1000:

Первая матрица: Oа #.
Вторая матрица: Oа B.

Используя следующий код, первая матрица заняла 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("");
}

В чем причина столь разного времени выполнения?


Как предлагается в комментариях, печать System.out.print("#");занимает всего 7.8871несколько секунд, а System.out.print("B");дает still printing....

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

Условия испытаний:

  • Я запустил этот тест из Netbeans 7.2 с выводом в консоль
  • Я использовал System.nanoTime()для измерений

62
Попробуйте изменить rand.nextInt (4) == 0 на i <250, чтобы исключить эффект генератора случайных чисел. У вас может закончиться энтропия, которая замедляет случайное поколение
fejese

3
Кажется, что оба работают на моей машине одинаковое количество времени, ~ 4 секунды.
Сотириос Делиманолис

155
если вы предполагаете, что печать B занимает больше времени, чем печать # .... почему вы не пытаетесь напечатать все B & all # вместо того, чтобы полагаться на случайную переменную r
Kakarot

18
Основываясь на принятом ответе, вы, очевидно, не пытались запустить его с перенаправленным выводом в файл или / dev / null.
Бармар

24
@fejese, Random () не криптографический рнг и поэтому не использует пул энтропии.
Раздели

Ответы:


4073

Чистое предположение заключается в том, что вы используете терминал, который пытается выполнять перенос слов, а не перенос символов, и обрабатывает его Bкак символ слова, а не #как символ слова. Поэтому, когда он достигает конца строки и ищет место для разрыва линии, он видит #почти сразу и счастливо разрывает там; в то время как с помощью B, он должен продолжать поиск дольше, и может иметь больше текста для переноса (что может быть дорого на некоторых терминалах, например, вывод пробелов, затем вывод пробелов для перезаписи переносимых букв).

Но это чистое предположение.


560
Это на самом деле правильный ответ! Добавление пробела после Bрешает его.
Куба Спатни

261
Есть некоторые ответы, которые приходят из усвоенного опыта. TJ и я (так как мы друзья) выросли во времена Apple] [и zx80 / ​​81. Тогда не было встроенного переноса слов. Итак, мы оба написали свои собственные - не раз. И эти уроки остаются с вами, они сжигаются в вашем мозгу ящерицы. Но если вы склонялись к коду после этого, когда ваше окружающее слово упаковывает все, или вы делаете это вручную до выполнения, вам будет сложнее столкнуться с проблемами с переносом слов.
JockM

315
Блестящий вывод. Но мы должны обобщить этот урок и всегда измерять производительность с исключенным выводом, направленным в / dev / null (NUL в Windows) или хотя бы в файл. Отображение на любом виде консоли, как правило, является очень дорогим вводом-выводом и всегда искажает время - даже если не так драматично, как это.
Боб Кернс

37
@MrLister: System.out.printlnне выполняет перенос слов; то, что он выводил, - это перенос слов (и блокировка, поэтому System.out.printlnпришлось подождать).
TJ Crowder

35
@Chris - на самом деле, я буду утверждать, что не печатать их - это решение проблемы получения точного времени алгоритма. Каждый раз, когда вы печатаете на консоль (любого рода), вы вызываете все виды внешней обработки, не связанные с тем, на чем вы тестируете производительность. Это ошибка в вашей процедуре измерения, чистая и простая. С другой стороны, если вы рассматриваете проблему не как измерение, а как понимание несоответствия, тогда да, не печать - это уловка отладки. Это сводится к тому, какую проблему вы пытаетесь решить?
Боб Кернс

210

Я провел тесты на Eclipse vs Netbeans 8.0.2, оба с Java версии 1.8; Я использовал System.nanoTime()для измерений.

Затмение:

Я получил одинаковое время в обоих случаях - около 1,564 секунды .

Netbeans:

  • Использование «#»: 1,536 секунды
  • Использование «B»: 44,164 секунды

Таким образом, похоже, что Netbeans имеет плохую производительность при печати на консоль.

После дополнительных исследований я понял, что проблема заключается в переносе строки в максимальном буфере Netbeans (он не ограничен 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 имеет проблему переноса строк, которая может быть решена (потому что проблема не возникает в затмении) (без добавления пробела после B («B»)).

32
Можете ли вы уточнить свои исследовательские стратегии, а затем, что в конечном итоге привело вас к выводу, что виновником является перенос строк? (Мне любопытно, что у вас за детективные навыки!)
silph 30.10.16

12

Да, виновник определенно перенос слов. Когда я тестировал две ваши программы, среда IDE NetBeans 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("");                
}

Еще один совет - менять настройки среды IDE NetBeans. Прежде всего, перейдите в Инструменты NetBeans и нажмите Параметры . После этого нажмите « Редактор» и перейдите на вкладку « Форматирование ». Затем выберите Anywhere в Line Wrap Option. Компиляция программы займет почти на 6,24% меньше времени.

Настройки редактора NetBeans

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.