Что такое bottleneck
Что проверяют
Умеете ли вы искать причину медленной работы измерением, а не догадкой. Для Python ответ обязан упереться в природу нагрузки: узкое место у счётной задачи и у задачи ожидания лечится противоположными инструментами.
Как отвечать
Bottleneck это участок, который ограничивает пропускную способность всей системы: остальное может быть сколь угодно быстрым, итог определяет он. Отсюда сразу следствие, которое формализует закон Амдала: оптимизация не узкого места не даёт почти ничего, сколько сил в неё ни вложи.
Порядок работы всегда один: измерить, найти, исправить, измерить снова. Инструменты в Python называйте конкретно: cProfile по функциям, line_profiler по строкам, py-spy чтобы снять профиль с живого процесса без остановки, tracemalloc по памяти, а на уровне сервиса трассировка запросов и лог медленных обращений к базе.
Дальше классификация, ради которой вопрос и задают. Счётная задача, упирающаяся в процессор, от потоков не ускорится: глобальная блокировка интерпретатора пускает в байткод один поток за раз, нужен multiprocessing, векторизация через numpy или уход в расширение на С. Задача, упирающаяся в ожидание сети, диска или базы, потоками и asyncio ускоряется отлично: на время ожидания блокировка отпускается.
И честное наблюдение: в типичном веб-сервисе узкое место чаще не язык, а база и походы по сети, то есть запросы в цикле, отсутствующий индекс и выборка лишних колонок.
Где спотыкаются
Оптимизируют по интуиции. Переписывают красивый участок, который и так занимает доли процента времени, и не замечают одного запроса в цикле рядом. Без профиля любое «стало быстрее» это вера, а не результат.
Считают виноватым сам язык и предлагают переписать сервис. Обычно между текущим состоянием и переписыванием лежат кэш, индекс, пакетная обработка и убранная лишняя сериализация.
Лечат счётную задачу через asyncio или потоки. Событийный цикл помогает ждать, а не считать: тяжёлая функция в корутине блокирует весь цикл и делает сервису хуже, чем было.