Программа значительно медленнее при использовании из TTY

Итак, у меня есть программа, написанная на C++.

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

Я только что заметил, что если я запускаю программу на той же самой машине, все вычисления, если они начнутся с TTY, будут занимать около 20-21 секунды, и только около 0,2 секунды, если я запускаю ее с терминала GNOME.

Что вызывает это? Это буквально тот же файл на той же машине.

1 ответ

Решение

Некоторая справочная теория

То, с чем вы работаете после CTRL+ALT+F1 и GNOME Terminal, - это разные реализации одной и той же концепции: эмуляция так называемого полноэкранного терминала.

Первая вещь называется виртуальным терминалом (VT) в Linux или обычно просто "консоль". Он использует специальный текстовый режим "только для текста", который по-прежнему предоставляется аппаратными видеокартами на x86-совместимых платформах (то есть в наследстве "IBM PC"). Последний представляет собой приложение с графическим интерфейсом.

Оба предоставляют приложениям, работающим с их помощью, набор средств, которые такие приложения ожидают от "оконечного устройства" (подробности и дополнительные указания - здесь).

Проблема под рукой

Хорошо, теперь давайте перейдем к воспринимаемой медлительности.

Я уверен, что суть вашей проблемы в том, что ваша программа выполняет так называемый "блокирующий" ввод / вывод. То есть каждый раз, когда вы делаете что-то вроде

std::cout << "Hello, world" << endl;

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

После определенной обработки (и чаще всего некоторой буферизации) эти данные должны фактически покинуть запущенный процесс вашей программы и фактически получить вывод на любой носитель, на который ваши программы отправляют свой вывод. В Linux (и других Unix-совместимых системах) это требует обращения к ядру - через выделенный системный вызов (или сокращенно syscall) с именем write(),

Таким образом, C++ stdlib в конечном итоге делает это write() syscall, а затем ожидает его завершения, то есть ждет, пока ядро ​​ответит: "Хорошо, получатель данных сказал, что он его получил".

Как вы можете сделать вывод, получателем данных, которые выводит ваша программа, является терминал (эмулятор), на котором выполняется ваша программа - либо Linux VT, либо экземпляр терминала GNOME в ваших тестах. (Полная картина сложнее, поскольку ядро ​​не будет отправлять данные прямо в работающий эмулятор терминала, но давайте не будем усложнять описание.)

И так скорость, с которой это write() syscall завершает работу в значительной степени зависит от того, как быстро обработчик данных обрабатывает его! В вашем случае GNOME Terminal просто делает это быстрее.

Мое предположение заключается в том, что драйвер VT должным образом рендерит все передаваемые ему данные, прокручивает их и т. Д., В то время как GNOME Terminal оптимизирует пакеты входящих данных, отображая только их хвостовую часть (независимо от размера экрана терминала), и помещает отдых в так называемом "буфере прокрутки", который есть в большинстве эмуляторов терминала с графическим интерфейсом.

На вынос, чтобы сделать

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

Обратите внимание, что ввод / вывод сложен: ваш процесс может быть прерван (остановлен, если его ресурсы переданы другому процессу) операционной системой в любое время, когда он собирается подождать, пока какой-либо ресурс ввода-вывода станет доступным для записи, например, на жестком диске.,

Таким образом, верный способ измерить "сырую" производительность вычислений состоит в том, чтобы в вашей программе была возможность отключить все операции ввода-вывода. Если это невозможно или было бы слишком уродливо для реализации, по крайней мере попробуйте направить весь вывод на так называемое "нулевое устройство", /dev/null, запустив вашу программу как

$ ./program >/dev/null

Нулевое устройство просто отбрасывает все данные, переданные ему. Так что да, каждый раунд ввода-вывода, выполняемый stdlib C++, будет попадать в ядро, но, по крайней мере, вы будете иметь почти постоянную (и мгновенную) скорость записи.

Если вам нужны как меры, так и сгенерированные данные, рассмотрите возможность создания так называемого RAM-диска и перенаправления вывода в файл, расположенный там.

Еще один способ измерения: обратите внимание, что даже в кажущейся неактивной системе, работающей на обычной ОС (такой как Ubuntu или что-то еще), ЦП никогда не спит - всегда есть некоторые задачи, выполняющие работу в фоновом режиме. Это означает, что измерение производительности вычислений даже без какого-либо ввода-вывода или с "отключенным" вводом-выводом (как описано выше) будет по-прежнему давать разные результаты при каждом запуске.

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

Другие вопросы по тегам