Наглядная демонстрация модуля timeit
Вы когда-нибудь задумывались, медленнее ли вызывать функцию с ключевыми словами, чем без них? Другими словами: что быстрее; позиционные аргументы (myfunc('mike', 33)
) или kwargs (myfunc(name='mike', age=33)
)?
В этой короткой и простой статье мы узнаем, стоит ли жертвовать удобочитаемостью передачи аргументов ключевых слов по сравнению с передачей аргументов позиционно. Настраиваем бенчмарк с модулем timeit
и сравниваем результаты. Ничего слишком сложного; давайте кодировать!
Функция
Чтобы оценить производительность вызовов функций, нам сначала нужна функция, которую мы можем вызвать:
def the_func(arg1, arg2): pass
Функция не содержит ничего, кроме pass
, что означает, что сама по себе функция ничего не делает. Это гарантирует, что мы можем выделить и сравнить способ вызова функции (то есть позиционно или с помощью kwargs).
Сценарий бенчмаркинга
Далее нам понадобится код, который вызывает функцию несколько раз и записывает время, необходимое для ее выполнения:
import timeit number = 25_000_000 repeat = 10 times_pos: [float] = timeit.repeat(stmt="func('hello', 'world')", globals={'func': the_func}, number=number, repeat=repeat) times_kwarg: [float] = timeit.repeat(stmt="func(arg1='hello', arg2='world')", globals={'func': the_func}, number=number, repeat=repeat)
Здесь на помощь приходит модуль timeit
. Мы используем timeit.repeat
для определения времени запуска функции number
раз. Мы определяем вызов функции в stmt
и сопоставляем func
в stmt с функцией, которую мы определили ранее, используя globals
dict. Затем мы повторяем эксперимент несколько раз, используя аргумент repeat
. В итоге мы получаем массив из 10 чисел с плавающей запятой, каждое из которых представляет выполнение вызова функции 25 миллионов раз.
Проверка результатов
В приведенном ниже коде мы берем список чисел с плавающей запятой, которые нам предоставляет timeit
, и отображаем минимальное, максимальное и среднее время выполнения.
print("\t\t\t min (s) \t max (s) \t avg (s)") print(f"pos: \t\t {min(times_pos):.5f} \t {max(times_pos):.5f} \t {sum(times_pos) / len(times_pos):.5f}") print(f"arg only: \t {min(times_arg_only):.5f} \t {max(times_arg_only):.5f} \t {sum(times_arg_only) / len(times_arg_only):.5f}")
Сделанный! Давайте проведем сравнительный анализ!
Результаты
Результаты в:
min (s) max (s) avg (s) pos: 1.38941 1.72278 1.58808 kwarg: 1.72834 1.76344 1.75132 min (s) max (s) avg (s) pos: 2.07883 2.77485 2.35694 kwarg: 2.05186 3.05402 2.74669
Кажется, что в среднем позиционные аргументы быстрее на 0,39 секунды или чуть более 16,5%. Однако помните, что позиционные аргументы на 0,39 секунды быстрее вызывают функцию 25 миллионов раз.
Это означает, что выбор позиционных аргументов вместо аргументов ключевого слова сэкономит вам около 16 наносекунд; время, за которое свет проходит расстояние около 4,8 метра (16 футов).
Заключение
Начнем с главного вывода: не прекращайте использовать kwargs из соображений производительности! Лично мне нравится использовать аргументы с ключевыми словами, потому что это делает мой код удобочитаемым и уменьшает смешение аргументов. В этой статье мы увидели, что использование аргументов ключевых слов приводит к незначительному увеличению производительности. Получите удобочитаемость по цене потери скорости в несколько наносекунд.
Я надеюсь, что эта статья была настолько ясной, насколько я надеюсь, но если это не так, пожалуйста, дайте мне знать, что я могу сделать, чтобы прояснить ситуацию. А пока ознакомьтесь с моими другими статьями на всевозможные темы, связанные с программированием, например:
- Git для абсолютных новичков: понимание Git с помощью видеоигры
- Создайте и опубликуйте свой собственный пакет Python
- Создайте быстрый автоматически документированный, удобный в сопровождении и простой в использовании Python API в 5 строках кода с помощью FastAPI
Удачного кодирования!
— Майк
P.S. Нравится, что я делаю? Следуй за мной!