Оптимизация задержки и пропускной способности
Проверить первоначальная версия этой статьи в моем блоге разработчиков.
Каждый разработчик знаком с терминами задержка (иначе время запроса) и пропускная способность (или req/s) в отношении веб-приложений.
Мы все инстинктивно знаем, что они взаимосвязаны: если мне удастся уменьшить нагрузку на сервер, необходимую для выполнения одного запроса, пропускная способность также возрастет. Конечно, если определенный запрос требует Икс количество работы ЦП (назовем это ЦП-секундами), затем — игнорируя ввод-вывод и другие задержки — каждый запрос будет завершен примерно через Икс секунд, а пропускная способность С-основная машина будет С/Х запросы каждую секунду.
Мы можем видеть это визуально здесь (для простоты я буду игнорировать async
и подобные эффекты):
Чего большинство людей не осознает, так это того, что эти взаимовыгодные и проигрышные отношения верно только до тех пор, пока каждый запрос обрабатывается в одном потоке от начала до конца (или прыгая между потоками, но никогда не работая параллельно). Как только вы начинаете распараллеливать запрос, прямая связь разрывается и вы представляете компромисс между желаемым вашим конечным пользователем задержка и ваш аудитор эффективности максимальная пропускная способность меры. Давайте изменим приведенный выше пример, чтобы обрабатывать запрос параллельно на всех 4 ядрах.
Теоретически параллелизм выглядит здесь как святой Грааль, позволяя нам сократить время запросов в четыре раза в нашем примере с 4 ЦП:
Осознайте здесь, как прямо пропорциональная связь просто нарушилась: Наши запросы стали намного короче без каких-либо изменений в максимальной пропускной способности. (Поскольку каждый запрос теперь занимает Х/4 секунд, он использует 4 ядра ЦП вместо 1. Но это все еще идеально, верно? Мы значительно увеличили задержку, а пропускная способность не изменилась, звучит здорово!
За исключением того, что это был теоретически вид параллелизмакоторый вы никогда не сможете идеально выполнить на реальной машине. Каждое параллельное выполнение имеет некоторые накладные расходы. То, что раньше было простым циклом и некоторой работой над каждым, теперь становится Разделение, Очередь, Дождитесь доступного потока пула потоков (возможно, создайте его), Выполните работу, Присоединитесь.
Это более реалистичное представление того, что произошло бы в реальности:
Как видите, наша задержка все же уменьшилась (на 50%), но наша пропускная способность тоже уменьшилась! из-за накладных расходов.
Теперь, стоит ли это того, бизнес-решение. Фактическая сумма накладных расходов сильно варьируется и зависит как от архитектуры программного обеспечения, методов кодирования, так и от внешних факторов. Я не собираюсь вдаваться в подробности в этой статье, но чтение любой статьи, основанной на TPL/параллелизме (включая некоторые здесь, в моем блоге), поможет, если вам нужно немного освежить в памяти. Конечно, вы всегда можете исправить пропускную способность, «бросая аппаратное обеспечение на проблему» (также известное как горизонтальное масштабирование), но это может не стоить затрат с точки зрения бизнеса.
Моя точка зрения на сегодня заключается в том, что существует компромисс, связанный с распараллеливанием вычислений в веб-API., и вы должны активно решать, стоит ли уменьшение задержки снижения пропускной способности. Для некоторых случаев это так, для некоторых это не так.
Если вам понравилась эта статья, не стесняйтесь проверить мой Блог разработчиков где я делюсь другими историями, подобными этой.
Если у вас есть какие-либо отзывы или вы хотите узнать больше по теме со мной на сеансе наставничества 1: 1, не стесняйтесь обращаться ко мне!