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