Performance Bottlenecks

Pause

Performance bottleneck (рус. узко́е ме́сто произво́дительности) — компонент или ресурс вычислительной системы, который ограничивает общую производительность системы пределом ниже возможностей остальных её частей[1]. Название восходит к аналогии с горловиной бутылки: как узость горлышка не позволяет вылить содержимое сразу, так и медленный компонент не даёт системе использовать остальные ресурсы полностью[2]. Понятие применяется к программному и аппаратному обеспечению: ограничивающим элементом может оказаться процессор, оперативная память, дисковый ввод-вывод, сеть или база данных[1].

Узкое место проявляется через растущие очереди запросов и задержки отклика при малой загрузке остальных компонентов[1]. Работа с ним строится как цикл: измерение показателей, профилирование программы, устранение найденного ограничения и повторное измерение[3]. Типовые приёмы устранения включают кэширование, добавление индексов в базе данных, оптимизацию алгоритмов и балансировку нагрузки[4][5].

Предел улучшений описывает закон Амдала: ускорение системы ограничено её последовательной частью, поэтому после устранения одного ограничения роль узкого места переходит к следующему компоненту[1]. В эксплуатационной практике узкие места выявляются средствами мониторинга и нагрузочного тестирования; для интернет-служб насыщение компонентов входит в число базовых показателей наблюдаемости[6]. Дата появления и авторство термина доступными источниками не установлены: слово закрепилось в инженерном языке без единого первоисточника, поэтому характеристика понятия опирается на учебную и справочную литературу по производительности систем[1][7].

От общего понятия «узкое место», охватывающего бизнес-процессы и организации, предмет статьи отличается областью применения: речь идёт о производительности вычислительных систем и программ[2]. От теории ограничений — управленческой методологии — понятие отличается инструментальной, инженерной природой: узкое место производительности измеряется метриками и находится инструментами профилирования[3]. От смежного термина «бутылочное горлышко» отличается только языком: в русскоязычной литературе оба выражения используются как синонимы[8].

Общие сведения
Что важно знать
Узкое место производительности
Область использования программное обеспечение, вычислительная система, оптимизация
Ключевые слова узкое место производительности, пропускная способность, профилирование, кэш, закон Амдала
Базовые понятия алгоритм, очередь, масштабируемость, закон Литтла, нагрузочное тестирование

Понятие

Термин

В англоязычном термине bottleneck прослеживается та же аналогия с бутылочным горлышком: при увеличении его ширины растёт и скорость опустошения бутылки[2]. В русскоязычной литературе наряду с калькой «бутылочное горлышко» употребляется и выражение «узкое место»[8]. В контексте производительности узким местом называют компонент, ограничивающий пропускную способность или время отклика системы даже при существенном росте возможностей остальных компонентов[1].

Понятие не привязано к уровню стека: ограничивающим элементом описывают и отдельную функцию программы, и операционную систему, и аппаратную платформу, и облачную инфраструктуру[1]. Практики выделяют «известные неизвестные» — ресурсы, за которыми наблюдают заранее, — и «неизвестные неизвестные», когда ограничение возникает во взаимодействии элементов, о которых разработчик не подозревал[1]. Поэтому методики анализа производительности начинаются с измерения, а не с догадок: исправление без измерений нередко ускоряет участок кода, который не был ограничением[3].

Проявление

Понятие производительности при этом многозначно: в литературе его раскрывают через два аспекта — продуктивность, то есть объём информации, обрабатываемой системой в единицу времени, и реактивность, то есть время между поступлением входных данных и появлением результата[9]. Один и тот же компонент может быть узким местом для реактивности и не быть им для продуктивности: пакетная обработка допускает очереди, тогда как интерактивный отклик требует их отсутствия[9].

Характерный признак узкого места — насыщение: с ростом нагрузки запросы накапливаются в очередях перед ограничивающим компонентом, время отклика растёт, а пропускная способность системы перестаёт увеличиваться[6]. Связь между числом запросов в системе, скоростью их поступления и временем обработки описывает закон Литтла; на его основе по измеренным метрикам оценивают, какой компонент насыщается первым[1]. В веб-проектах таким компонентом может оказываться сериализация данных: разбор и сборка структур занимали заметную часть времени обработки запросов в одном из описанных случаев[10].

Для приложений производительность связана с соглашениями об уровне сервиса: тестирование производительности и проводится для того, чтобы убедиться, что система отвечает требованиям к времени отклика и пропускной способности до того, как ограничение почувствуют пользователи[7]. В распределённых системах цена узкого места выше, чем в одномашинной программе: операции над данными проходят через сеть, репликацию и согласование между узлами, и неоптимальное размещение данных делает ограничением сам обмен[11].

Последствия зависят от назначения системы: для интерактивных служб узкое место означает медленный отклик и отток пользователей, для пакетной обработки — срыв сроков, для серверов приложений — исчерпание пулов соединений и отказы в обслуживании[7]. Исследователи отмечают, что проведение тестов помогает выявить ограничения, упущенные на этапе проектирования[10].

undefined

Виды узких мест

Процессор и память

Вычислительным называют ограничение, при котором задачи ждут процессорного времени: загрузка процессоров близка к максимуму, а остальные подсистемы простаивают[1]. Такой режим характерен для программ с плотными вычислениями: обработки изображений, компиляции, научных расчётов. Ограничение по памяти проявляется иначе: система тратит время на выделение и освобождение памяти, подкачку страниц на диск и работу сборщиков мусора в управляемых средах вроде Java[1]. Устойчивый уход в подкачку приводит к лавинообразному падению отклика, поскольку дисковые операции становятся посредником между программой и памятью.

Ограничение может создаваться и подсистемой памяти процессора: доступ к оперативной памяти занимает на порядки больше тактов, чем к кэшу процессора, поэтому промахи кэша превращают формально быстрый код в медленный[1]. Средства профилирования, учитывающие счётчики производительности процессора, позволяют отличить такие промахи от нехватки вычислений[1].

В управляемых средах узким местом может стать и алгоритм управления памятью: частые аллокации и сборка мусора создают паузы и лишнюю нагрузку на процессор[8]. Средством анализа служат профилировщики памяти и сборщики трассировок: они показывают, какие участки кода создают наибольшее число аллокаций[1].

Дисковый ввод-вывод и сеть

Ограничение по диску возникает, когда операции ввода-вывода стоят в очереди к накопителю: жёсткие диски с подвижными головками чувствительны к случайным обращениям, а твердотельные накопители снимают часть этих ограничений[1]. Файловая система добавляет собственные задержки: журналирование, блокировки и кэширование страниц меняют фактический порядок операций[1]. Сетевое ограничение связано с исчерпанием канала либо с ожиданиями в стеке сетевых протоколов: повторные передачи и управление потоком не ускоряются программой[1]. Распределённые системы добавляют задержки межмашинного обмена, и время, невидимое в коде отдельного узла, становится основной частью отклика[11].

Базы данных

Для серверных приложений типовым ограничением служит база данных: медленные запросы, отсутствие индексов и блокировки при конкурентной записи[12]. Инструментом разбора служит план выполнения запроса: инструкция EXPLAIN в документации PostgreSQL показывает, как база данных намерена выполнить запрос — по индексу или последовательным просмотром[5]. Для запросов к реляционным базам данных таким же инструментом служит EXPLAIN: в документации MySQL он описан как способ показать план выполнения и подсказать, какие индексы следует добавить[13].

Классическая ошибка приложений — запросы в цикле, когда для списка объектов выполняется по одному запросу на объект; такие обращения множат задержки сети и перегружают базу. Для баз данных, не использующих SQL, приёмы аналогичны: в литературе о NoSQL-системах к основным методам повышения производительности запросов отнесены индексы, сегментирование данных по узлам, кэширование и репликация[14].

Отдельные ограничения создают блокировки: длительные транзакции удерживают строки и таблицы, и параллельные запросы выстраиваются в очередь[12].

Алгоритмическая сложность

Ограничение может лежать в самой программе: неоптимальный алгоритм переводит растущий объём данных в непропорционально растущее время работы[15]. Анализ вычислительной сложности позволяет оценить, как изменится время при росте входа, и заменить квадратичные обходы, например, на хеш-таблицы с быстрым поиском[15]. Профилировщики при этом показывают «горячие» участки кода — функции, на которые уходит наибольшая доля времени выполнения[9]. В одном из описанных исследований именно профилирование показало, что значительное время занимает работа со структурами данных, а не ожидание диска[10].

undefined

Обнаружение

Мониторинг и метрики

Первый шаг поиска ограничения — измерение: без показателей нельзя ни подтвердить наличие узкого места, ни проверить эффект изменения[3]. Системы мониторинга собирают метрики загрузки процессоров, памяти, дисков и сети, а для сервисов отслеживают четыре базовых сигнала: задержку, трафик, ошибки и насыщение[6]. Насыщение — степень, в которой компонент занят относительно своих пределов, — прямо указывает на кандидатов в узкие места[6]. Показатели собираются в дашборды служб и сравниваются между версиями: рост времени отклика после выпуска изменения — сигнал о возвращившемся ограничении[6]. Записи журналов и распределённые трассировки дополняют метрики: они показывают путь отдельного запроса через службу, где суммируются задержки каждого участка[6].

Нагрузочное тестирование дополняет мониторинг: система проверяется имитацией работы множества пользователей на разделяемом ресурсе, что выявляет ограничения, пропущенные при обычной эксплуатации[12].

Профилирование

Профилировщики измеряют время выполнения функций и число вызовов, показывая, какие участки программы требуют внимания[9]. Инструментальные профилировщики перехватывают каждое событие и дают точные счётчики, но замедляют выполнение; семплирующие периодически снимают стек и дают оценку распределения времени с малыми накладными расходами[16]. В среде Linux стандартным семплирующим инструментом служит perf, а набор Valgrind исполняет программу в виртуальной машине с инструментацией и подсчитывает каждую инструкцию[17]. В средах с автоматическим управлением памятью профилировщик показывает и распределение аллокаций: для языка Go стандартный инструмент pprof собирает профили процессора и памяти прямо из работающей службы[18]. Сравнение профилей до и после правки подтверждает, что оптимизация попала в ограничение, а не в случайный участок[18].

Отладчики и трассировщики дополняют картину: они позволяют остановить программу на пике очереди или проследить отдельный запрос сквозь систему[1].

Нагрузочное тестирование

Нагрузочное тестирование имитирует работу приложения под реалистичной нагрузкой и проверяет, как система справляется с ростом числа пользователей или объёма данных[8]. Типовые инструменты — Apache JMeter и LoadRunner — генерируют большое количество запросов с нескольких машин под управлением одной[12]. Открытый инструмент Apache JMeter позволяет описывать сценарии нагрузки и строить распределённую генерацию запросов из графической среды[19].

Сценарии тестирования имитируют действия реальных пользователей: переходы по страницам, отправку форм и запросы к API[8]. Автоматизация позволяет масштабировать тесты, повторять их и быстро выявлять возникающие проблемы производительности[8].

undefined

Устранение

Оптимизация кода

После нахождения ограничения применяется набор приёмов, специфичный для вида ресурса. Вычислительные ограничения снимают переписыванием «горячих» функций, выбором более быстрых структур данных и параллелизмом. Ограничения по памяти устраняют уменьшением аллокаций и пулами объектов, дисковые — кэшированием и последовательной записью[1]. В базах данных типовое решение — добавление индексов под конкретные запросы, сокращение количества обращений и разбор плана выполнения[5].

Стратегии кэширования различаются частотой попаданий: в исследовании информационно-ориентированных сетей стратегии, оставляющие копии на промежуточных узлах, дали вероятность попадания объекта в кэш на 7—12 процентов выше относительно других схем при меньшем пути извлечения данных[20]. На веб-слое кэширование выносят в отдельный сервер: прокси-сервер NGINX кэширует ответы службы и отдаёт повторные запросы без обращения к приложению[21]. Цена кэширования — управление актуальностью: устаревшая копия в кэше хуже медленного запроса, поэтому время жизни записей и правила их сброса проектируют вместе с самим кэшем[11].

В описанном исследовании веб-приложения кэширование результатов запросов к базе данных уменьшило число обращений на 70 процентов, а кэширование страниц на стороне клиента дало 30-процентный прирост производительности[4]. Оптимизацию при этом рекомендуют применять точечно: правки следует направлять на участки, найденные профилировщиком, а не на весь код подряд[3].

undefined

Масштабирование и архитектура

Когда предел отдельного узла исчерпан, нагрузку распределяют: вертикальное масштабирование увеличивает мощность одного узла, горизонтальное добавляет узлы, распределяя нагрузку между ними, а балансировка нагрузки обеспечивает равномерное распределение запросов между компонентами[10]. Для баз данных применяют репликацию чтения и сегментирование данных по узлам; для межслужебных обменов — асинхронные очереди и кэширование ответов[11]. Архитектурные решения — выделение тяжёлых подсистем в отдельные службы и переход к микросервисной архитектуре — снижают взаимное влияние компонентов, но добавляют сетевые задержки, которые сами способны стать ограничением[10]. Аналогичная картина наблюдается в микросервисных проектах: насыщение смещается от вычислений к обменам между службами, и профили отдельной службы перестают объяснять задержки всей системы[10].

В облачной среде ограничения ресурсов проявляются через квоты и общие физические носители, поэтому показатели соседних виртуальных машин влияют на отклик[1].

Связь с другими механизмами

Закон Амдала и предел ускорения

Количественную рамку для ожиданий задаёт закон Амдала: если доля последовательных вычислений в задаче не меняется, то ускорение при добавлении исполнителей стремится к величине, обратной этой доле[1]. Отсюда следует правило узкого места: усиление компонента, который не ограничивает систему, не ускоряет систему; после устранения текущего ограничения начинает доминировать следующий компонент[1]. Поэтому производительность повышают итерациями, каждый раз находя новое узкое место измерением, а не одним большим рефакторингом[3]. Практика инженерии надёжности включает эту итерацию в повседневную работу: изменения производительности проверяются на нагрузочных стендах и регистрируются в показателях службы[6].

Отличие от смежных понятий

От общего «узкого места» предмет статьи отличается границей: речь идёт только о вычислительных системах и программном обеспечении, тогда как общее понятие охватывает процессы и организации[2]. От теории ограничений Голдратта — управленческой методологии поиска ограничения бизнес-системы — инженерное понятие отличается инструментарием: вместо перестройки процессов применяются метрики, профилирование и нагрузочные тесты[3]. От оптимизации как процесса улучшения программы узкое место отличается тем, что является объектом, а не действием: оптимизация направляется на найденное ограничение[9]. Профилирование и нагрузочное тестирование служат средствами обнаружения узких мест, но не исчерпывают понятия: ограничение может лежать вне программы — в оборудовании, сети или конфигурации[1].

Ограничения и критика

Смещение узкого места

Устранение одного ограничения смещает нагрузку на следующий компонент, поэтому результат серии улучшений всегда ограничен очередным узким местом[1]. Системные исследователи описывают случаи, когда после устранения дискового ограничения ограничением становилась сеть, а после её расширения — база данных: система меняет режим насыщения, но не перестаёт иметь узкое место[11]. По этой причине единичные замеры «до» и «после» недостаточны: автоматизация нагрузочного тестирования позволяет легко повторять тесты и быстро выявлять возникающие проблемы[8]. В описанном случае высоконагруженного веб-проекта ограничение сериализации сняли предсериализацией редко меняемых данных, что заметно сократило время обработки запросов; деление же приложения на командную и запросную части, по наблюдению автора, повышает производительность и гибкость системы[10].

В распределённых системах к этому добавляется переменность поведения: профиль нагрузки, который был репрезентативным при вводе системы в эксплуатацию, устаревает с ростом данных и пользователей[11].

undefined

Преждевременная оптимизация

Отраслевые рекомендации предостерегают от оптимизации без измерений: переработка кода в расчёте на скорость без подтверждённого ограничения усложняет программу и часто не даёт эффекта[3]. Систематический анализ алгоритмов и структур данных, восходящий к трудам Дональда Кнута, отделяет обоснованные улучшения от произвольных: сначала оценивается сложность и профиль выполнения, затем принимается решение о переписывании[15]. Часть микрооптимизаций не даёт эффекта из-за трансляторов: современные компиляторы и среды выполнения самостоятельно переупорядочивают код и устраняют лишние операции, поэтому победа в микротесте может исчезать после сборки релиза[1]. Измерения в изолированных микротестах также искажают картину: без воспроизведения реального профиля нагрузки выигрыш не переносится на систему целиком[1].

Критики также отмечают цену самих оптимизаций: кэши требуют управления актуальностью, репликация — согласования копий, а усложнённый код хуже сопровождается[11]. Поэтому в инженерной культуре закрепилось правило: сначала измерить, затем изменить, после чего измерить снова и сравнить результаты[3].

undefined

Примечания

  1. ↑ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 Gregg B. Systems Performance: Enterprise and the Cloud (англ.). — Pearson. — New York, 2020. — 928 p. — ISBN 9780136820154.
  2. ↑ 1 2 3 4 Максимизация производительности: Техники и технологии для создания высокопроизводительных систем. Хабр. habr.com. Дата обращения: 8 октября 2026.
  3. ↑ 1 2 3 4 5 6 7 8 9 Производительность и оптимизация: основы и практические рекомендации. SciLead. scilead.ru. Дата обращения: 8 октября 2026.
  4. ↑ 1 2 Ботыгин И. А., Каликин К. А. Исследование методов увеличения производительности WEB-приложений // Известия Томского политехнического университета. — 2008. — Т. 312, № 5.
  5. ↑ 1 2 3 PostgreSQL: Documentation: 14.1. Using EXPLAIN (англ.). PostgreSQL Documentation. postgresql.org. Дата обращения: 8 октября 2026.
  6. ↑ 1 2 3 4 5 6 7 Beyer B., Jones C., Petoff J., Murphy N. R. Site Reliability Engineering: How Google Runs Production Systems (англ.). — O'Reilly Media. — Sebastopol, 2016. — 552 p. — ISBN 9781491929124.
  7. ↑ 1 2 3 Molyneaux I. The Art of Application Performance Testing (англ.). — O'Reilly Media. — Sebastopol, 2009. — 272 p. — ISBN 9781491900543.
  8. ↑ 1 2 3 4 5 6 7 Бевзенко С. А. Процесс автоматизации нагрузочного тестирования веб-систем: основные аспекты // Universum: технические науки. — 2023. — № 8 (113).
  9. ↑ 1 2 3 4 5 Елисеев А. С., Гитман М. Б. Анализ производительности программного обеспечения при помощи математического планирования эксперимента // Обработка сплошных и слоистых материалов. — 2011. — № 1.
  10. ↑ 1 2 3 4 5 6 7 Шумилов М. И. Оптимизация высоконагруженных веб-проектов с использованием микросервисной архитектуры // Universum: технические науки. — 2024. — Т. 2, № 11 (128).
  11. ↑ 1 2 3 4 5 6 7 Kleppmann M. Designing Data-Intensive Applications (англ.). — O'Reilly Media. — Sebastopol, 2017. — 616 p. — ISBN 9781449373320.
  12. ↑ 1 2 3 4 Басов А. С. Обзор механизмов тестирования баз данных // Вестник науки. — 2020. — № 7 (28).
  13. ↑ MySQL 8.0 Reference Manual: 10.8.1 Optimizing Queries with EXPLAIN (англ.). MySQL Documentation. dev.mysql.com. Дата обращения: 8 октября 2026.
  14. ↑ Громов К. И. Методы оптимизации запросов в базах данных структуры NoSQL // Cold Science. — 2024. — № 10.
  15. ↑ 1 2 3 Knuth D. E. The Art of Computer Programming, Volume 1: Fundamental Algorithms (англ.). — Addison-Wesley. — Boston, 1997. — 672 p. — ISBN 9780201896831.
  16. ↑ perf(1) — Performance analysis tools for Linux (англ.). Linux manual pages. man7.org. Дата обращения: 8 октября 2026.
  17. ↑ Callgrind: a call-graph generating cache and branch prediction profiler (англ.). Valgrind Documentation. valgrind.org. Дата обращения: 8 октября 2026.
  18. ↑ 1 2 Profiling Go Programs (англ.). The Go Blog. go.dev. Дата обращения: 8 октября 2026.
  19. ↑ Apache JMeter — User's Manual: Getting Started (англ.). Apache JMeter. jmeter.apache.org. Дата обращения: 8 октября 2026.
  20. ↑ Путевская И. В. Исследование эффективности стратегий кэширования данных в информационно-ориентированных сетях // Вестник Чувашского университета. — 2024. — № 4.
  21. ↑ A Guide to Caching with NGINX (англ.). NGINX Community Blog. nginx.com. Дата обращения: 8 октября 2026.

Литература

Дополнительно по теме

© Правообладателем данного материала является АНО «Интернет-энциклопедия «РУВИКИ».
Использование данного материала на других сайтах возможно только с согласия АНО «Интернет-энциклопедия «РУВИКИ».
Pause