Printf замедляет мою программу

У меня есть небольшая программа для вычисления хэшей (для хэш-таблиц). Код выглядит довольно чистым, надеюсь, но есть что-то не связанное с этим, которое меня подталкивает.

Я могу с легкостью генерировать около миллиона хэшей примерно через 0,2-0,3 секунды (сравнивается с /usr/bin/time ). Тем не менее, когда я печатаю их (f) в цикле for, программа замедляется примерно до 5 секунд.

  • Почему это?
  • Как сделать это быстрее? mmapp() может быть?
  • Как проектируется stdlibc в отношении этого и как его можно улучшить?
  • Как ядро ​​может поддерживать его лучше? Как его нужно модифицировать, чтобы сделать пропускную способность на локальных "файлах" (сокетах, трубах и т.д.) ДЕЙСТВИТЕЛЬНО быстро?

Я с нетерпением жду интересных и подробных ответов. Спасибо.

PS: это для набора инструментов для компилятора, так что не стесняйтесь вникать в детали. Хотя это не имеет ничего общего с самой проблемой, я просто хотел указать, что детали меня интересуют.

Добавление

Я ищу более программные подходы к решениям и объяснениям. В самом деле, трубопровод выполняет эту работу, но я не контролирую то, что делает "пользователь".

Конечно, сейчас я тестирую, что не будет сделано "обычными пользователями". НО это не меняет того факта, что простой printf() замедляет процесс, что является проблемой, я пытаюсь найти оптимальное программное решение для.


Добавление - Удивительные результаты

Ориентировочное время для обычных вызовов printf() внутри TTY и занимает около 4 минут 20 секунд.

Тестирование под /dev/pts (например, Konsole) ускоряет вывод до 5 секунд.

Требуется примерно столько же времени при использовании setbuffer() в моем тестовом коде до размера 16384, почти то же самое для 8192: около 6 секунд.

setbuffer() имеет , очевидно, никакого эффекта при его использовании: он занимает одинаковое время (на TTY около 4 минут, на PTS около 5 секунд).

Удивительная вещь, если я начинаю тест на TTY1, а затем переключаюсь на другой TTY, он принимает то же самое, что и в PTS: около 5 секунд.

Заключение: ядро ​​делает что-то, что связано с доступностью и удобством для пользователя. Да!

Обычно он должен быть одинаково медленным, независимо от того, смотрите ли вы в TTY при его активном действии или переходите к другому TTY.


Урок: при запуске программ с интенсивным доступом переключитесь на другой TTY!

Ответы

Ответ 1

Небуферизованный выход очень медленный.

По умолчанию stdout полностью буферизуется, однако при подключении к терминалу stdout небуферируется или буферизируется по строке.

Попробуйте включить буферизацию для stdout с помощью setvbuf(), например:

char buffer[8192];

setvbuf(stdout, buffer, _IOFBF, sizeof(buffer));

Ответ 2

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

При выводе на консоль прокрутка обычно является убийцей.

Ответ 3

Если вы используете printf() на консоли, он обычно очень медленный. Я не уверен, почему, но я считаю, что он не возвращается, пока консоль не отобразит окончательную строку. Кроме того, вы не можете mmap() в stdout.

Запись в файл должна быть намного быстрее (но на порядки медленнее, чем вычисление хеша, все операции ввода-вывода медленны).

Ответ 4

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

Ответ 5

  • I/O всегда медленный по сравнению с прямое вычисление. Система имеет дождаться появления большего количества компонентов доступны для их использования. Это то придется ждать ответа прежде чем он сможет продолжить. Наоборот если он просто вычисляет, то это только действительно движущиеся данные между RAM и CPU.

  • Я не тестировал это, но может быть быстрее добавить хеши в строку, а затем просто напечатать строку в конце. Хотя, если вы используете C, а не С++, это может оказаться больно!

3 и 4 находятся вне меня, я боюсь.

Ответ 6

  • Почему бы не создать строки по требованию, а не на месте строительства? Нет смысла выводить 40 экранов данных за одну секунду, как вы можете их прочитать? Почему бы не создать выход по мере необходимости и просто отобразить последний экран, а затем, если требуется, пользователь прокручивает???

  • Почему бы не использовать sprintf для печати в строку, а затем построить конкатенированную строку всех результатов в памяти и напечатать в конце?

  • Перейдя на sprintf, вы можете четко видеть, сколько времени потрачено на преобразование формата и сколько потрачено на отображение результата на консоль и соответствующим образом изменить код.

  • Консольный выход по определению медленный, создание хэша управляет только несколькими байтами памяти. Консольный выход должен проходить через многие уровни операционной системы, у которых будет код для обработки блокировки потоков/процессов и т.д., Когда он в конечном итоге попадет на драйвер дисплея, который может быть устройством с 9600 бод! или большой растровый дисплей, простые функции, такие как прокрутка экрана, могут включать в себя манипулирование мегабайтами памяти.

Ответ 7

Поскольку I/O всегда намного медленнее вычисления ЦП, вы можете сначала сохранить все значения в самом быстром вводе-выводе. Поэтому используйте ОЗУ, если у вас достаточно, используйте файлы, если нет, но он намного медленнее, чем оперативная память.

Распечатка значений теперь может быть выполнена впоследствии или параллельно другим потоком. Поэтому потоку (вычислениям) расчета может не потребоваться ждать, пока printf вернется.

Ответ 8

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

Кроме того, кто собирается их читать? Там нет смысла печатать их все в удобочитаемом формате, если никто не должен их читать.

Ответ 9

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

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

Возможно, лучше конкатенировать строки до тех пор, пока не будет достигнут определенный предел, а затем, когда это будет, напишите все сразу. Или даже используя pthreads для выполнения желаемого выполнения процесса.

Отредактировано: Что касается 2,3, то это вне меня. Для 4, Я не знаком с Sun, но знаю и перепутал с Solaris, Может быть опция ядра для использования виртуального tty.. Я признаю, что это было время, с конфигурациями ядра и перекомпиляцией. Таким образом, моя память может быть невелика в этом, у вас есть корень с параметрами для просмотра.

[email protected]:/usr/src/linux $ make; make menuconfig **OR kconfig if from X**

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

Отредактировано: , но там вы настраиваете ядро, добавляя файл в файловую систему proc (если такая вещь существует) или, возможно, коммутатор, переданный в ядро, что-то вроде этого (это творческий подход и не подразумевает его фактически существует), fastio Забастовкa >

Надеюсь, это поможет, С наилучшими пожеланиями, Том.