Программа ЗНАЧИТЕЛЬНО медленнее при использовании с TTY

Программа ЗНАЧИТЕЛЬНО медленнее при использовании с TTY

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

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

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

Что является причиной? Это буквально тот же самый файл на том же самом компьютере.

решение1

Некоторые теоретические основы

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

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

Оба предоставляют приложениям, работающим с их помощью, набор возможностей, которые такие приложения ожидают от «терминального устройства» (более подробная информация и дополнительные указания —здесь).

Проблема на данный момент

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

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

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

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

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

Поэтому библиотека C++ stdlib в конечном итоге выполняет этот write()системный вызов, а затем ждет его завершения, то есть ждет, пока ядро ​​скажет: «ОК, получатель данных сообщил, что он их получил».

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

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

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

Что нужно сделать на вынос

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

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

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

$ ./program >/dev/null

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

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

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

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

Связанный контент