4

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

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

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

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

1 ответ1

5

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

$ ./program >/dev/null

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

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

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

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

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .