CProfile и pstats: найдите доминирующую операцию в профиле Python
Напишите решение на Python и проверьте его на странице.
Другие упражнения по этой теме: Профилирование Python: задачи на cProfile, pstats и байт-код dis.
Доминирующая операция профиля
Модуль мониторинга профилирует вызов функции с помощью cProfile.Profile и разбирает собранные замеры в словаре stats.stats объекта pstats.Stats. Чтобы выявить узкие места, анализатор ищет потенциальные листовые операции — записи профиля, у которых кумулятивное время cumtime численно равно собственному времени tottime, включая случаи с нулевыми значениями.
Для каждого кандидата детектор вычисляет отношение его tottime к общему суммарному времени всех вызовов total_tt. Лидер определяется только тогда, когда величина total_tt строго больше нуля, а максимальная найденная доля строго превышает заданный порог threshold. Если наибольшую долю делят сразу несколько операций, предпочтение отдаётся претенденту с меньшим количеством вызовов ncalls, а при повторном равенстве — кандидату с лексикографически меньшим кортежем (filename, line, func_name).
Если подходящих записей не нашлось или общее время total_tt равно нулю, результат считается неопределённым. Выясните, какая листовая операция доминирует в предоставленной статистике pstats.Stats, или убедитесь, что ни один кандидат не удовлетворяет условиям отбора.
Что уже дано
В редакторе объявлена функция dominant_profile_operation(stats, threshold), которую вам предстоит реализовать.
Вспомогательная функция make_profile_case(config) запускает профилирование тестовых функций с управляемым таймером и доступна при выполнении кода; реализовывать генераторы или эмуляторы профилей самостоятельно не требуется.
Что нужно сделать
Реализуйте функцию dominant_profile_operation(stats, threshold).
Функция принимает два аргумента:
stats— объектpstats.Stats, содержащий от 1 до 500 записей профиля в словареstats.statsи атрибутstats.total_tt(неотрицательное конечное число до 1 000 000);threshold— конечное число типаintилиfloatиз отрезка[0, 1].
Ключами словаря stats.stats являются стандартные кортежи идентификации (filename, line, func_name), где filename и func_name — строки, а line — целое число. Значениями являются кортежи вида (primitive_calls, total_calls, tottime, cumtime, callers), где total_calls отражает суммарное число вызовов ncalls (целое число от 1 до 10 000), а tottime и cumtime — конечные неотрицательные числа до 1 000 000.
Правила отбора доминирующей операции:
- Кандидатами признаются только те записи, у которых кумулятивное время численно равно собственному:
cumtime == tottime(включая равенство нулю). Наличие или отсутствие реальных дочерних вызовов не проверяется. - Анализируйте только переданный объект статистики. Запрещено повторно запускать функции, модифицировать ключи, счётчики или времена в
stats.stats. - Записи встроенных функций и стандартных функций Python равноправны.
- Если
stats.total_tt == 0, функция должна вернутьNone. - Доля кандидата вычисляется как
tottime / stats.total_tt. - Среди всех кандидатов находится максимальная доля. Если она не превышает
thresholdстрого (максимальная доля > threshold), операция считается недоминирующей, и функция возвращаетNone. - Если максимальную долю имеют несколько кандидатов, побеждает кандидат с наименьшим общим значением
ncalls(total_calls). - При равенстве долей и количества вызовов выбирается кандидат с лексикографически меньшим полным кортежем ключа
(filename, line, func_name). - При успешном выборе функция возвращает точный кортеж
(filename, line, func_name)победившей операции. Если ни одного подходящего кандидата нет, возвращаетсяNone.
Ввод и вывод
Стандартные потоки ввода stdin и вывода stdout в решении не используются: считывать ввод и вызывать print() не нужно. После выполнения кода учащегося проверяющая система считывает из пространства имён модуля функцию верхнего уровня dominant_profile_operation и вызывает её с проверочными аргументами.
В приведённом ниже примере профилируются функции с дискретным таймером. Из четырёх зарегистрированных операций равенство cumtime == tottime выполняется только у двух листовых: a_leaf (tottime = 3.0, cumtime = 3.0) и z_leaf (tottime = 1.0, cumtime = 1.0). При общем времени total_tt = 4.0 доля a_leaf составляет 3.0 / 4.0 = 0.75, а доля z_leaf — 1.0 / 4.0 = 0.25. Поскольку максимальная доля 0.75 строго превышает порог 0.4, побеждает операция a_leaf, и функция возвращает полный кортеж её идентификатора.
Пример использования
probe = make_profile_case({
'z_calls': 1, 'z_cost': 1, 'z_file': 'example_ops.py', 'z_line': 10,
'a_calls': 1, 'a_cost': 3, 'a_file': 'example_ops.py', 'a_line': 20,
})
stats = probe.stats
for key, row in sorted(stats.stats.items(), key=lambda item: item[0][2]):
primitive_calls, total_calls, tottime, cumtime, callers = row
print(key[2], total_calls, tottime, cumtime)
print(stats.total_tt)
print(dominant_profile_operation(stats, 0.4))
Вывод:
a_leaf 1 3.0 3.0
dispatcher 1 0.0 4.0
driver 1 0.0 4.0
z_leaf 1 1.0 1.0
4.0
('example_ops.py', 20, 'a_leaf')
Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.
Отправьте решение, чтобы увидеть результаты тестов.
Связанные задачи
- CProfile Python: разница между tottime и cumtime в отчёте
- Модуль dis Python: найдите инструкции возврата в байт-коде
- Модуль dis Python: аудит повторяющихся инструкций LOADGLOBAL
- Удаление ключа из ChainMap в Python: проверка поведения del
- Обратный индекс на Python: группировка уникальных значений через defaultdict
- Подсчёт дефицита ресурсов в Python: квоты и суммы заявок
