Что означают термины «связанный с процессором» и «связанный с вводом / выводом»?
Что означают термины «связанный с процессором» и «связанный с вводом / выводом»?
Ответы:
Это довольно интуитивно понятно:
Программа привязана к ЦП, если она будет работать быстрее, если бы ЦП был быстрее, т.е. она тратит большую часть своего времени, просто используя ЦП (делая вычисления). Программа, которая вычисляет новые цифры π, как правило, будет привязана к процессору, она просто сокращает числа.
Программа связана с вводом / выводом, если она будет работать быстрее, если подсистема ввода / вывода будет быстрее. Какая именно система ввода / вывода подразумевается, может варьироваться; Я обычно ассоциирую это с диском, но, конечно, сеть или общение в целом тоже распространены. Программа, которая просматривает огромный файл для некоторых данных, может быть привязана к вводу / выводу, поскольку узким местом является чтение данных с диска (на самом деле, этот пример, возможно, является старомодным в наши дни с сотнями МБ / с). из SSD).
CPU Bound означает, что скорость, с которой идет процесс, ограничена скоростью процессора. Задача, которая выполняет вычисления для небольшого набора чисел, например, умножения небольших матриц, скорее всего, связана с процессором.
Ограничение ввода / вывода означает, что скорость, с которой происходит процесс, ограничена скоростью подсистемы ввода / вывода. Задача, которая обрабатывает данные с диска, например, подсчет количества строк в файле, скорее всего, связана с вводом / выводом.
Ограничение памяти означает, что скорость, с которой идет процесс, ограничена объемом доступной памяти и скоростью доступа к этой памяти. Задача, которая обрабатывает большие объемы данных в памяти, например, умножает большие матрицы, скорее всего, связана с памятью.
Кэш-привязка означает скорость, с которой прогресс процесса ограничен объемом и скоростью доступного кэша. Задача, которая просто обрабатывает больше данных, чем помещается в кеш, будет привязана к кешу.
Bound I / O будет медленнее, чем Bound Memory, будет медленнее, чем Cache Bound, будет медленнее, чем CPU Bound.
Решение быть связанным с вводом / выводом не обязательно, чтобы получить больше Памяти. В некоторых ситуациях алгоритм доступа может быть разработан с учетом ограничений ввода-вывода, памяти или кэша. См. Алгоритмы кеширования .
Многопоточность
В этом ответе я расскажу об одном важном случае использования различий между CPU и IO: при написании многопоточного кода.
Пример, связанный с вводом / выводом в ОЗУ: векторная сумма
Рассмотрим программу, которая суммирует все значения одного вектора:
#define SIZE 1000000000
unsigned int is[SIZE];
unsigned int sum = 0;
size_t i = 0;
for (i = 0; i < SIZE; i++)
/* Each one of those requires a RAM access! */
sum += is[i]
Распараллеливание того, что разделение массива поровну для каждого из ваших ядер имеет ограниченную полезность на современных современных настольных компьютерах.
Например, на моем Ubuntu 19.04, ноутбуке Lenovo ThinkPad P51 с процессором: ЦП Intel Core i7-7820HQ (4 ядра / 8 потоков), ОЗУ: 2x Samsung M471A2K43BB1-CRC (2x 16 ГБ). Я получаю следующие результаты:
Обратите внимание, что между прогонами есть большое расхождение. Но я не могу значительно увеличить размер массива, так как я уже на 8 ГБ, и у меня нет настроения для статистики по нескольким прогонам сегодня. Однако это выглядело как типичный прогон после многих ручных прогонов.
Код теста:
pthread
Исходный код POSIX C, используемый в графике.
А вот версия C ++, которая дает аналогичные результаты.
Я не знаю достаточно компьютерной архитектуры, чтобы полностью объяснить форму кривой, но ясно одно: вычисления не становятся в 8 раз быстрее, чем наивно ожидалось, потому что я использую все свои 8 потоков! По некоторым причинам, 2 и 3 потоки были оптимальными, а добавление большего просто замедляет процесс.
Сравните это с работой с привязкой к процессору, которая на самом деле работает в 8 раз быстрее: что означают 'real', 'user' и 'sys' в выводе time (1)?
Причина в том, что все процессоры совместно используют одну шину памяти, соединяющуюся с RAM:
CPU 1 --\ Bus +-----+
CPU 2 ---\__________| RAM |
... ---/ +-----+
CPU N --/
поэтому узким местом становится шина памяти, а не процессор.
Это происходит потому, что добавление двух чисел занимает один цикл ЦП, а чтение памяти занимает около 100 циклов ЦП в аппаратном обеспечении 2016 года.
Таким образом, процессорная работа, выполняемая на байт входных данных, слишком мала, и мы называем это процессом, связанным с IO.
Единственный способ ускорить дальнейшие вычисления - это ускорить индивидуальный доступ к памяти с помощью нового аппаратного обеспечения памяти, например многоканальной памяти .
Например, обновление до более быстрой тактовой частоты процессора не будет очень полезным.
Другие примеры
Умножение матриц связано с использованием ОЗУ и графических процессоров. Вход содержит:
2 * N**2
номера, но:
N ** 3
умножения сделаны, и этого достаточно, чтобы распараллеливание стоило того для большого практического N.
Вот почему существуют параллельные библиотеки умножения матриц ЦП, подобные следующим:
Использование кэша имеет большое значение для скорости реализации. Посмотрите, например, этот пример сравнения дидактического графического процессора .
Смотрите также:
Сеть является прототипом IO-связанного примера.
Даже когда мы отправляем один байт данных, для достижения цели все равно требуется много времени.
Распараллеливание небольших сетевых запросов, таких как HTTP-запросы, может значительно повысить производительность.
Если сеть уже загружена (например, загружается торрент), распараллеливание может увеличить задержку (например, вы можете загрузить веб-страницу «одновременно»).
Фиктивная операция с привязкой к процессору C ++, которая принимает одно число и сильно его сокращает:
Похоже, что сортировка выполняется с использованием ЦП на основе следующего эксперимента: реализованы ли уже параллельные алгоритмы C ++ 17? который показал увеличение производительности в 4 раза для параллельной сортировки, но я хотел бы также получить более теоретическое подтверждение
Как узнать, связаны ли вы с процессором или IO
IO без ОЗУ привязан как диск, сеть:, ps aux
тогда theck, если CPU% / 100 < n threads
. Если да, вы привязаны к IO, например, блокирующие read
s просто ждут данных, и планировщик пропускает этот процесс. Затем используйте другие инструменты, например, sudo iotop
чтобы решить, какой именно IO является проблемой.
Или, если выполнение выполняется быстро и вы параметризуете количество потоков, вы можете легко увидеть, time
что производительность увеличивается по мере увеличения количества потоков для работы с привязкой к ЦП: что означают «реальные», «пользовательские» и «sys» в выход времени (1)?
Ограничение RAM-IO: сложнее сказать, так как время ожидания RAM включено в CPU%
измерения, см. Также:
Некоторые варианты:
Графические процессоры
У графических процессоров есть узкое место ввода-вывода при первой передаче входных данных из обычной оперативно-читаемой памяти ОЗУ в графический процессор.
Следовательно, графические процессоры могут быть лучше, чем центральные, для приложений, связанных с процессором.
Однако, как только данные передаются в графический процессор, он может работать с этими байтами быстрее, чем процессор, потому что графический процессор:
имеет большую локализацию данных, чем большинство процессорных систем, поэтому доступ к данным для некоторых ядер быстрее, чем для других
использует параллелизм данных и жертвует задержкой, просто пропуская любые данные, которые не готовы к немедленной обработке.
Поскольку графический процессор должен работать с большими параллельными входными данными, лучше просто перейти к следующим доступным данным, а не ждать поступления текущих данных и заблокировать все другие операции, как это обычно делает ЦП.
Поэтому графический процессор может быть быстрее центрального процессора, если ваше приложение:
Эти варианты дизайна изначально были нацелены на применение 3D-рендеринга, основные шаги которого приведены в разделе Что такое шейдеры в OpenGL и для чего они нам нужны?
и поэтому мы заключаем, что эти приложения связаны с процессором.
С появлением программируемого GPGPU мы можем наблюдать несколько приложений GPGPU, которые служат примерами операций с привязкой к процессору:
Обработка изображений с помощью шейдеров GLSL?
Локальные операции обработки изображений, такие как размытие фильтра, очень параллельны по своей природе.
Можно ли построить тепловую карту из точечных данных со скоростью 60 раз в секунду?
Построение графиков тепловой карты, если построенная функция достаточно сложна.
https://www.youtube.com/watch?v=fE0P6H8eK4I Хесус Мартин Берланга "Динамика жидкости в реальном времени: процессор против графического процессора"
Решение дифференциальных уравнений в частных производных, таких как уравнение динамики жидкости Навье-Стокса :
Смотрите также:
CPython Global Intepreter Lock (GIL)
В качестве краткого примера я хочу указать на Python Global Lock Interpreter (GIL): что такое глобальная блокировка интерпретатора (GIL) в CPython?
Эта деталь реализации CPython не позволяет нескольким потокам Python эффективно использовать работу с процессором. Документы CPython говорят:
Детали реализации CPython: В CPython из-за Глобальной блокировки интерпретатора только один поток может выполнять код Python одновременно (даже если некоторые ориентированные на производительность библиотеки могут преодолеть это ограничение). Если вы хотите, чтобы ваше приложение более эффективно использовало вычислительные ресурсы многоядерных машин, рекомендуется использовать
multiprocessing
илиconcurrent.futures.ProcessPoolExecutor
. Тем не менее, многопоточность по-прежнему является подходящей моделью, если вы хотите запустить несколько задач, связанных с вводом / выводом одновременно.
Таким образом, здесь у нас есть пример, когда содержимое, связанное с процессором, не подходит, а ограничение ввода-вывода.
ЦП означает , что программа упираются в CPU, или центрального блока обработки данных , в то время ввода / вывода связанных средством программа упираются ввода / вывода или ввода / вывода, такие как чтение или запись на диск, сеть и т.д.
В общем, при оптимизации компьютерных программ стараются найти узкое место и устранить его. Знание того, что ваша программа связана с процессором, помогает, так что не нужно без необходимости оптимизировать что-то еще.
[Под «узким местом» я подразумеваю то, что заставляет вашу программу работать медленнее, чем это было бы в противном случае.]
Еще один способ выразить ту же идею:
Если ускорение процессора не ускоряет вашу программу, это может быть связано с вводом / выводом .
Если ускорение ввода-вывода (например, с использованием более быстрого диска) не помогает, возможно, ваша программа связана с процессором.
(Я использовал «может быть», потому что вам нужно учитывать другие ресурсы. Одним из примеров является память).
Когда ваша программа ожидает ввода-вывода (т. Е. Чтение / запись на диск или чтение / запись по сети и т. Д.), Процессор может выполнять другие задачи, даже если ваша программа остановлена. Скорость вашей программы будет в основном зависеть от того, насколько быстро может произойти ввод-вывод, и если вы хотите ускорить ее, вам нужно будет ускорить ввод-вывод.
Если ваша программа выполняет много программных инструкций и не ожидает ввода-вывода, то она называется связанной с процессором. Ускорение процессора заставит программу работать быстрее.
В любом случае, ключ к ускорению программы может заключаться не в ускорении аппаратного обеспечения, а в оптимизации программы для уменьшения количества операций ввода-вывода или ЦП, в которых она нуждается, или в том, чтобы она выполняла ввод-вывод, в то время как она также интенсивно использует ЦП. вещи.
Граница ввода / вывода относится к состоянию, при котором время, необходимое для завершения вычисления, определяется главным образом периодом, затраченным на ожидание завершения операций ввода / вывода.
Это противоположно тому, что задача связана с процессором. Это обстоятельство возникает, когда скорость, с которой запрашиваются данные, ниже, чем скорость, с которой они потребляются, или, другими словами, больше времени затрачивается на запрос данных, чем на их обработку.
Посмотрите, что говорит Microsoft.
Ядром асинхронного программирования являются объекты Task и Task, которые моделируют асинхронные операции. Они поддерживаются ключевыми словами async и await. Модель довольно проста в большинстве случаев:
Для кода, связанного с I / O, вы ожидаете операцию, которая возвращает Task или Task внутри асинхронного метода.
Для кода с привязкой к ЦП вы ожидаете операцию, которая запускается в фоновом потоке с помощью метода Task.Run.
Ключевое слово await - вот где происходит волшебство. Он предоставляет управление вызывающей стороне метода, который выполнялся в ожидании, и в конечном итоге позволяет пользовательскому интерфейсу быть отзывчивым или эластичным для службы.
Пример ввода-вывода: загрузка данных из веб-службы
private readonly HttpClient _httpClient = new HttpClient();
downloadButton.Clicked += async (o, e) =>
{
// This line will yield control to the UI as the request
// from the web service is happening.
//
// The UI thread is now free to perform other work.
var stringData = await _httpClient.GetStringAsync(URL);
DoSomethingWithData(stringData);
};
Пример с привязкой к процессору: выполнение расчета для игры
private DamageResult CalculateDamageDone()
{
// Code omitted:
//
// Does an expensive calculation and returns
// the result of that calculation.
}
calculateButton.Clicked += async (o, e) =>
{
// This line will yield control to the UI while CalculateDamageDone()
// performs its work. The UI thread is free to perform other work.
var damageResult = await Task.Run(() => CalculateDamageDone());
DisplayDamage(damageResult);
};
Приведенные выше примеры показали, как вы можете использовать async и await для работы с I / O и CPU. Это ключ, который вы можете определить, когда работа, которую вам нужно выполнить, связана с вводом-выводом или с процессором, потому что это может сильно повлиять на производительность вашего кода и потенциально может привести к неправильному использованию определенных конструкций.
Вот два вопроса, которые вы должны задать, прежде чем писать какой-либо код:
Будет ли ваш код чего-то «ждать», например, данные из базы данных?
- Если ваш ответ «да», то ваша работа связана с вводом / выводом.
Будет ли ваш код выполнять очень дорогие вычисления?
- Если вы ответили «да», то ваша работа связана с процессором.
Если ваша работа связана с вводом / выводом, используйте async и ожидайте без Task.Run . Вы не должны использовать Task Parallel Library. Причина этого изложена в статье Async in Depth .
Если ваша работа связана с процессором, и вы заботитесь об отзывчивости, используйте async и await, но порождайте работу в другом потоке с помощью Task.Run. Если работа подходит для параллелизма и параллелизма, вам также следует рассмотреть возможность использования библиотеки параллельных задач .
Приложение привязано к ЦП, когда производительность арифметической / логической / с плавающей запятой (A / L / FP) во время выполнения в основном близка к теоретической пиковой производительности процессора (данные, предоставленные производителем и определяемые характеристиками процессор: количество ядер, частота, регистры, ALU, FPU и т. д.).
Быстродействие очень сложно достичь в реальных приложениях, не говоря уже о невозможности. Большинство приложений обращаются к памяти в разных частях выполнения, и процессор не выполняет операции A / L / FP в течение нескольких циклов. Это называется ограничением фон Неймана из-за расстояния, которое существует между памятью и процессором.
Если вы хотите приблизиться к пиковой производительности ЦП, стратегия может заключаться в том, чтобы попытаться повторно использовать большую часть данных в кэш-памяти, чтобы избежать запроса данных из основной памяти. Алгоритм, который использует эту особенность, является умножением матрицы на матрицу (если обе матрицы могут быть сохранены в кэш-памяти). Это происходит потому, что если матрицы имеют размер, n x n
то вам нужно работать с 2 n^3
операциями, используя только 2 n^2
числа FP. С другой стороны, добавление матрицы, например, является менее связанным с ЦП или более связанным с памятью приложением, чем умножение матрицы, поскольку для этого требуются только n^2
FLOP с одинаковыми данными.
На следующем рисунке показаны FLOP, полученные с помощью наивных алгоритмов сложения матриц и умножения матриц в Intel i5-9300H:
Обратите внимание, что, как и ожидалось, производительность умножения матриц больше, чем сложение матриц Эти результаты могут быть воспроизведены путем запуска test/gemm
и test/matadd
доступны в этом хранилище .
Я предлагаю также посмотреть видео, данное Дж. Донгаррой об этом эффекте.
Связанный процесс ввода / вывода: - Если большая часть времени жизни процесса проводится в состоянии ввода / вывода, то этот процесс является процессом, связанным с вводом / выводом. Пример: -calculator, Internet Explorer
Процесс, связанный с процессором: - Если большая часть срока службы процессируется в процессоре, то это процесс, связанный с процессором.