Timeit.timeit: примеры (PYTHON)

Использование timeit.timeit для анализа производительности кода
Раздел: Тестирование производительности, Замер времени
timeit.timeit(stmt: str, setup: str, number: int): float

Описание функции timeit.timeit

Функция timeit.timeit() из стандартного модуля timeit применяется для измерения времени выполнения небольших фрагментов кода. Её основное назначение - сравнительный анализ производительности различных подходов или микрооптимизаций. Результат возвращается в виде числа с плавающей точкой, обозначающего общее время в секундах для заданного количества выполнений измеряемого кода.

Аргументы функции

  • stmt (по умолчанию 'pass'): Строка с кодом, время выполнения которого необходимо измерить. Может содержать несколько инструкций, разделённых точкой с запятой.
  • setup (по умолчанию 'pass'): Строка с кодом, который выполняется один раз перед началом измерений. Обычно здесь размещают импорты модулей или подготовку данных.
  • timer: Объект таймера. В большинстве случаев используется значение по умолчанию time.perf_counter, которое обеспечивает высокую точность.
  • number (по умолчанию 1000000): Количество повторений выполнения кода из аргумента stmt. Функция возвращает общее время для всех повторений.
  • globals (по умолчанию None): Словарь с глобальными переменными, в контексте которых будет выполняться код. Позволяет использовать функции и переменные из текущей области видимости.

Возвращаемое значение

Функция возвращает значение типа float - общее время в секундах, затраченное на выполнение кода из stmt указанное количество раз (number).

Краткие примеры использования

Простой вызов

Измерение времени создания списка.

import timeit
result = timeit.timeit(stmt="[i for i in range(100)]")
print(result)
5.234567890123456

Использование параметра setup

import timeit
code = '''
result = []
for i in range(100):
    result.append(i)
'''
setup_code = "import random"
result = timeit.timeit(stmt=code, setup=setup_code, number=10000)
print(result)
0.4567890123456789

Изменение количества выполнений (number)

import timeit
result = timeit.timeit(stmt="pow(2, 100)", number=500000)
print(result)
0.7890123456789012

Работа с глобальными переменными через globals

import timeit
def my_func():
    return sum(range(1000))

result = timeit.timeit(stmt="my_func()", globals=globals(), number=1000)
print(result)
0.12345678901234567

Использование собственного таймера

import timeit, time
result = timeit.timeit(stmt="time.sleep(0.001)", setup="import time", timer=time.process_time, number=100)
print(result)
0.0012345678901234567

Похожие функции в Python

timeit.repeat

Функция timeit.repeat() принимает те же аргументы, что и timeit(), но также параметр repeat. Она выполняет измерение несколько раз и возвращает список результатов. Полезна для оценки стабильности времени выполнения.

Модуль profile и cProfile

Модули profile и cProfile предназначены для профилирования всего кода, а не только небольших фрагментов. Они показывают время выполнения каждой функции, включая вложенные вызовы, что помогает найти узкие места в программе.

time.perf_counter и time.process_time

Функции time.perf_counter() и time.process_time() возвращают текущее время с высокой точностью. Их используют для ручного измерения интервалов, оборачивая участок кода в вызовы до и после. perf_counter учитывает время сна системы, а process_time - только процессорное время текущего процесса.

Аналоги функции в других языках программирования

PHP: microtime

$start = microtime(true);
for ($i = 0; $i < 1000000; $i++) { /* код */ }
$time = microtime(true) - $start;
echo $time;
0.12345600128174

JavaScript: console.time и performance.now

console.time('test');
for (let i = 0; i < 1000000; i++) { /* код */ }
console.timeEnd('test');
// Или
const start = performance.now();
// код
const duration = performance.now() - start;
test: 12.345 ms

Java: System.nanoTime

long start = System.nanoTime();
for (int i = 0; i < 1000000; i++) { /* код */ }
long duration = System.nanoTime() - start;
System.out.println(duration / 1_000_000_000.0);
0.123456789

C#: Stopwatch

using System.Diagnostics;
var sw = Stopwatch.StartNew();
for (int i = 0; i < 1000000; i++) { /* код */ }
sw.Stop();
Console.WriteLine(sw.Elapsed.TotalSeconds);
0.1234567

Go: time.Since

package main
import (
    "fmt"
    "time"
)
func main() {
    start := time.Now()
    for i := 0; i < 1000000; i++ { /* код */ }
    elapsed := time.Since(start)
    fmt.Println(elapsed.Seconds())
}
0.123456789

Lua: os.clock

local start = os.clock()
for i=1,1000000 do -- код end
local time = os.clock() - start
print(time)
0.123456

Kotlin: measureTimeMillis

import kotlin.system.measureTimeMillis
val time = measureTimeMillis {
    for (i in 1..1000000) { /* код */ }
}
println(time / 1000.0)
0.123

Типичные ошибки при использовании

Измерение кода с побочными эффектами

Повторное выполнение кода, который изменяет состояние, может привести к некорректным результатам или ошибкам.

import timeit
counter = 0
result = timeit.timeit(stmt="counter += 1", globals=globals(), number=10)
print(f"Время: {result}, Счётчик: {counter}")
Время: 1.234567890123456e-06, Счётчик: 10

Неверное использование globals для импорта

Если в коде stmt используются модули, их нужно импортировать либо в setup, либо передать через globals.

import timeit
# Ошибка: NameError: name 'math' is not defined
result = timeit.timeit(stmt="math.sqrt(4)", number=1000)
NameError: name 'math' is not defined

Слишком малое значение number

Малое количество повторений может давать нестабильные результаты из-за погрешности таймера.

import timeit
result = timeit.timeit(stmt="pass", number=1)
print(result)
1.234567890123456e-07

Измерение очень медленного кода с большим number

Установка чрезмерно большого числа повторений для долгого кода может надолго заблокировать выполнение программы.

Изменения в последних версиях Python

В Python 3.5 был добавлен параметр globals, что упростило использование функции в контексте текущего модуля.

Начиная с Python 3.8, по умолчанию в качестве таймера используется time.perf_counter с наивысшей доступной точностью. В более ранних версиях на Windows использовался time.clock, который устарел.

Расширенные примеры использования

Сравнение производительности двух операций

Пример python
import timeit
list_comp = timeit.timeit(stmt="[x for x in range(1000)]", number=10000)
list_func = timeit.timeit(stmt="list(range(1000))", number=10000)
print(f"Генератор списка: {list_comp}")
print(f"Функция list: {list_func}")
Генератор списка: 0.4567890123456789
Функция list: 0.23456789012345678

Использование с пользовательскими функциями и аргументами

Пример python
import timeit
def calculate_power(base, exp):
    return base ** exp

result = timeit.timeit(stmt="calculate_power(2, 100)", globals=globals(), number=500000)
print(result)
0.567890123456789

Многократный запуск с помощью repeat

Пример python
import timeit
results = timeit.repeat(stmt="sum(range(1000))", number=10000, repeat=5)
print(f"Результаты: {results}")
print(f"Лучшее время: {min(results)}")
Результаты: [0.123456, 0.123457, 0.123455, 0.123458, 0.123454]
Лучшее время: 0.123454

Измерение времени с использованием lambda-функций

Пример python
import timeit
result = timeit.timeit(stmt="(lambda x: x*x)(5)", number=1000000)
print(result)
0.09876543210987654

Использование timeit для оценки скорости доступа к атрибутам

Пример python
import timeit
class TestClass:
    attr = 1

obj = TestClass()
direct = timeit.timeit(stmt="obj.attr", globals=globals(), number=1000000)
getattr_call = timeit.timeit(stmt="getattr(obj, 'attr')", globals=globals(), number=1000000)
print(f"Прямой доступ: {direct}")
print(f"getattr: {getattr_call}")
Прямой доступ: 0.04567890123456789
getattr: 0.12345678901234567

Измерение в контексте Jupyter Notebook

В Jupyter доступна магическая команда %timeit.

Пример python
%timeit [x for x in range(1000)]
10000 loops, best of 5: 23.4 µs per loop

питон timeit.timeit function comments

En
Timeit.timeit Measure execution time