1. Einleitung
Das timeit-Modul ist ein standardmäßiges Python-Modul, das zur Leistungsanalyse von Python-Code verwendet wird. Es ermöglicht es Entwicklern, die Ausführungszeit von Codeblöcken mit hoher Präzision zu messen, was für die Optimierung von Anwendungen und das Finden von Engpässen entscheidend ist.
2. Grundlegende Funktionalitäten
2.1 Hauptfunktionen
Das Modul bietet zwei Hauptfunktionen:
timeit.timeit()– Misst die Zeit für eine einzelne Ausführung eines Code-Blockstimeit.repeat()– Führt das Timing mehrmals durch und gibt die Ergebnisse zurück
2.2 Klassen
timeit.Timer– Eine Klasse, die die Timing-Funktionalität kapselt und für wiederholte Messungen geeignet ist
3. Verwendung
3.1 Einfache Zeitmessung mit timeit.timeit()
import timeit
# Zeitmessung eines Code-Blocks
execution_time = timeit.timeit('sum([1, 2, 3, 4, 5])', number=100000)
print(f"Ausführungszeit: {execution_time:.6f} Sekunden")
3.2 Zeitmessung mit timeit.repeat()
import timeit
# Mehrfachmessung
times = timeit.repeat('sum([1, 2, 3, 4, 5])', number=100000, repeat=5)
print(f"Zeiten: {times}")
print(f"Minimum: {min(times):.6f} Sekunden")
3.3 Verwendung mit Timer-Klasse
import timeit
# Verwendung der Timer-Klasse
timer = timeit.Timer('sum([1, 2, 3, 4, 5])')
execution_time = timer.timeit(number=100000)
print(f"Ausführungszeit: {execution_time:.6f} Sekunden")
4. Parameter
4.1 timeit.timeit()
timeit.timeit(stmt='pass', setup='pass', timer=<default timer>, number=1000000)
Parameter:
stmt(str): Der auszuführende Code (Standard:'pass')setup(str): Setup-Code, der vor dem Timing ausgeführt wird (Standard:'pass')timer(callable): Ein Timer-Objekt, das die Zeit messen sollnumber(int): Anzahl der Ausführungen (Standard: 1.000.000)
4.2 timeit.repeat()
timeit.repeat(stmt='pass', setup='pass', timer=<default timer>, number=1000000, repeat=3)
Parameter:
stmt,setup,timer,number: Siehetimeit.timeit()repeat(int): Anzahl der Wiederholungen (Standard: 3)
5. Fortgeschrittene Verwendung
5.1 Timing von Funktionen
import timeit
def my_function():
return sum(range(100))
# Timing einer Funktion
execution_time = timeit.timeit(my_function, number=10000)
print(f"Funktion ausgeführt in: {execution_time:.6f} Sekunden")
5.2 Timing mit Setup-Code
import timeit
# Setup-Code wird nur einmal ausgeführt
setup_code = """
import random
data = [random.randint(1, 1000) for _ in range(1000)]
"""
test_code = """
sorted(data)
"""
execution_time = timeit.timeit(test_code, setup=setup_code, number=1000)
print(f"Sortierung ausgeführt in: {execution_time:.6f} Sekunden")
5.3 Timing mit Timer-Klasse für Wiederholungen
import timeit
def test_function():
return [x**2 for x in range(100)]
# Timer-Objekt für mehrfache Messungen
timer = timeit.Timer(test_function)
times = timer.repeat(repeat=5, number=1000)
print(f"Minimale Zeit: {min(times):.6f}s")
print(f"Durchschnittliche Zeit: {sum(times)/len(times):.6f}s")
6. Spezielle Features
6.1 Verwendung mit globals und locals
import timeit
def example():
x = 100
return x * 2
# Verwendung von globals
execution_time = timeit.timeit('example()', globals=globals(), number=10000)
print(f"Zeit mit globals: {execution_time:.6f} Sekunden")
6.2 Timing von Code in String-Format
import timeit
# Timing von Code als String
code = """
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
fibonacci(10)
"""
execution_time = timeit.timeit(code, number=1000)
print(f"Fibonacci berechnet in: {execution_time:.6f} Sekunden")
7. Verwendung von functools.partial für Funktionen mit Parametern
Die Verwendung von functools.partial ist besonders nützlich, wenn man Funktionen mit Parametern in timeit messen möchte. Dies ist notwendig, weil timeit Funktionen ohne Parameter aufrufen kann, aber manchmal Funktionen mit Parametern benötigt.
7.1 Grundlegende Verwendung von functools.partial
import timeit
from functools import partial
def power_function(base, exponent):
return base ** exponent
# Funktion mit Parametern mit partial
power_of_2 = partial(power_function, base=2)
# Timing der partiell angewendeten Funktion
execution_time = timeit.timeit(power_of_2, number=100000)
print(f"2^exponent ausgeführt in: {execution_time:.6f} Sekunden")
7.2 Timing von Funktionen mit mehreren Parametern
import timeit
from functools import partial
def calculate_area(length, width, unit='m²'):
return f"{length * width} {unit}"
# Partielle Anwendung mit mehreren Parametern
area_calc = partial(calculate_area, length=10, width=5)
# Timing der Funktion
execution_time = timeit.timeit(area_calc, number=100000)
print(f"Flächenberechnung ausgeführt in: {execution_time:.6f} Sekunden")
7.3 Komplexeres Beispiel mit Datenverarbeitung
import timeit
from functools import partial
def process_data(data, operation, multiplier=1):
"""Verarbeitet Daten mit einer bestimmten Operation"""
if operation == 'multiply':
return [x * multiplier for x in data]
elif operation == 'add':
return [x + multiplier for x in data]
return data
# Partielle Funktionen für verschiedene Operationen
multiply_data = partial(process_data, data=[1, 2, 3, 4, 5], operation='multiply', multiplier=2)
add_data = partial(process_data, data=[1, 2, 3, 4, 5], operation='add', multiplier=10)
# Timing der verschiedenen Operationen
multiply_time = timeit.timeit(multiply_data, number=100000)
add_time = timeit.timeit(add_data, number=100000)
print(f"Multiplikation ausgeführt in: {multiply_time:.6f} Sekunden")
print(f"Addition ausgeführt in: {add_time:.6f} Sekunden")
7.4 Timing von Klassenmethoden mit Parametern
import timeit
from functools import partial
class DataProcessor:
def __init__(self, data):
self.data = data
def filter_and_transform(self, threshold, multiplier):
return [x * multiplier for x in self.data if x > threshold]
# Instanz erstellen
processor = DataProcessor([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
# Partielle Funktion für spezifische Parameter
filtered_transform = partial(processor.filter_and_transform, threshold=5, multiplier=2)
# Timing
execution_time = timeit.timeit(filtered_transform, number=100000)
print(f"Filter- und Transformationsprozess ausgeführt in: {execution_time:.6f} Sekunden")
7.5 Benchmarking mit verschiedenen Parametern
import timeit
from functools import partial
def fibonacci(n, cache={}):
"""Fibonacci-Funktion mit Cache"""
if n in cache:
return cache[n]
if n <= 1:
return n
cache[n] = fibonacci(n-1, cache) + fibonacci(n-2, cache)
return cache[n]
# Partielle Funktionen für verschiedene Fibonacci-Zahlen
fib_10 = partial(fibonacci, n=10)
fib_20 = partial(fibonacci, n=20)
fib_30 = partial(fibonacci, n=30)
# Benchmarking
times = {}
for name, func in [('Fibonacci(10)', fib_10),
('Fibonacci(20)', fib_20),
('Fibonacci(30)', fib_30)]:
time = timeit.timeit(func, number=1000)
times[name] = time
print(f"{name}: {time:.6f} Sekunden")
# Ausgabe der Ergebnisse
for name, time in times.items():
print(f"{name}: {time:.6f} Sekunden")
7.6 Verwendung in Kombination mit anderen Modulen
import timeit
from functools import partial
import math
def complex_calculation(x, y, z):
"""Komplexe Berechnung mit mehreren Parametern"""
return math.sqrt(x**2 + y**2) * z + math.sin(x) - math.cos(y)
# Partielle Funktion für spezifische Werte
calc = partial(complex_calculation, x=1.5, y=2.5, z=3.0)
# Timing
execution_time = timeit.timeit(calc, number=100000)
print(f"Komplexe Berechnung ausgeführt in: {execution_time:.6f} Sekunden")
# Mehrfachmessung für genauere Ergebnisse
times = timeit.repeat(calc, number=10000, repeat=5)
print(f"Minimale Zeit: {min(times):.6f} Sekunden")
print(f"Durchschnittliche Zeit: {sum(times)/len(times):.6f} Sekunden")
8. Performance-Optimierungen
8.1 Vermeidung von Overhead durch Setup-Code
import timeit
from functools import partial
def my_function(data, multiplier):
return [x * multiplier for x in data]
# Daten vorbereiten
test_data = list(range(1000))
# Verwende partial um Parameter festzulegen
partial_function = partial(my_function, data=test_data, multiplier=2)
# Timing
execution_time = timeit.timeit(partial_function, number=10000)
print(f"Funktion mit partial ausgeführt in: {execution_time:.6f} Sekunden")
8.2 Vergleich verschiedener Ansätze
import timeit
from functools import partial
def process_list(data, operation, factor):
if operation == 'multiply':
return [x * factor for x in data]
elif operation == 'add':
return [x + factor for x in data]
# Daten vorbereiten
data = list(range(1000))
# Ansatz 1: String-Code
time1 = timeit.timeit('process_list(data, "multiply", 2)',
globals=globals(), number=1000)
# Ansatz 2: Partial-Funktion
partial_func = partial(process_list, data=data, operation='multiply', factor=2)
time2 = timeit.timeit(partial_func, number=1000)
print(f"String-Code: {time1:.6f}s")
print(f"Partial-Funktion: {time2:.6f}s")
print(f"Partial ist {time1/time2:.2f}x schneller" if time2 < time1 else f"String ist {time2/time1:.2f}x schneller")
9. Best Practices
9.1 Genauigkeit und Wiederholungen mit partial
import timeit
from functools import partial
def benchmark_with_parameters(func, *args, **kwargs):
"""Benchmark-Funktion mit Parametern"""
partial_func = partial(func, *args, **kwargs)
times = timeit.repeat(partial_func, number=1000, repeat=5)
return {
'times': times,
'min_time': min(times),
'mean_time': sum(times) / len(times),
'best_case': min(times)
}
# Beispiel
def expensive_calculation(x, y, z):
return sum(i**2 for i in range(x, y, z))
result = benchmark_with_parameters(expensive_calculation, 1, 100, 2)
print(f"Minimale Zeit: {result['min_time']:.6f}s")
print(f"Durchschnitt: {result['mean_time']:.6f}s")
9.2 Strukturierte Benchmarking-Funktion mit partial
import timeit
from functools import partial
def create_benchmark_suite(func, param_combinations):
"""
Erstellt eine Benchmark-Suite für verschiedene Parameterkombinationen
"""
results = {}
for name, params in param_combinations.items():
if isinstance(params, dict):
partial_func = partial(func, **params)
else:
partial_func = partial(func, *params)
times = timeit.repeat(partial_func, number=1000, repeat=3)
results[name] = {
'times': times,
'min_time': min(times),
'max_time': max(times),
'avg_time': sum(times) / len(times)
}
return results
# Beispielverwendung
def parameterized_function(a, b, c=1):
return sum(range(a, b, c))
# Parameterkombinationen
params = {
'small_range': {'a': 1, 'b': 100, 'c': 1},
'large_range': {'a': 1, 'b': 1000, 'c': 1},
'step_by_2': {'a': 1, 'b': 100, 'c': 2}
}
# Benchmark durchführen
results = create_benchmark_suite(parameterized_function, params)
for name, result in results.items():
print(f"{name}:")
print(f" Min: {result['min_time']:.6f}s")
print(f" Avg: {result['avg_time']:.6f}s")
print(f" Max: {result['max_time']:.6f}s")
10. Fehlerbehandlung
10.1 Umgang mit Fehlern bei partial-Funktionen
import timeit
from functools import partial
def risky_function(data, multiplier, divisor=1):
if divisor == 0:
raise ValueError("Divisor cannot be zero")
return [x * multiplier / divisor for x in data]
# Test mit korrekten Parametern
try:
safe_func = partial(risky_function, data=[1, 2, 3, 4, 5], multiplier=2, divisor=1)
time = timeit.timeit(safe_func, number=1000)
print(f"Zeit ohne Fehler: {time:.6f}s")
except Exception as e:
print(f"Fehler: {e}")
# Test mit fehlerhaften Parametern
try:
error_func = partial(risky_function, data=[1, 2, 3, 4, 5], multiplier=2, divisor=0)
time = timeit.timeit(error_func, number=1000)
print(f"Zeit mit Fehler: {time:.6f}s")
except Exception as e:
print(f"Fehler bei Timing: {e}")
11. Anwendungsfälle
11.1 Algorithmus-Vergleich mit Parametern
import timeit
from functools import partial
def bubble_sort(arr, reverse=False):
n = len(arr)
for i in range(n):
for j in range(0, n-i-1):
if (arr[j] > arr[j+1]) != reverse:
arr[j], arr[j+1] = arr[j+1], arr[j]
return arr
def quick_sort(arr, reverse=False):
if len(arr) <= 1:
return arr if not reverse else arr[::-1]
pivot = arr[len(arr) // 2]
left = [x for x in arr if x < pivot]
middle = [x for x in arr if x == pivot]
right = [x for x in arr if x > pivot]
result = quick_sort(left, reverse) + middle + quick_sort(right, reverse)
return result if not reverse else result[::-1]
# Daten vorbereiten
data = list(range(1000, 0, -1))
# Partielle Funktionen für verschiedene Sortieroptionen
bubble_asc = partial(bubble_sort, reverse=False)
bubble_desc = partial(bubble_sort, reverse=True)
quick_asc = partial(quick_sort, reverse=False)
quick_desc = partial(quick_sort, reverse=True)
# Vergleich der Algorithmen
times = {}
for name, func in [('Bubble Sort (asc)', bubble_asc),
('Bubble Sort (desc)', bubble_desc),
('Quick Sort (asc)', quick_asc),
('Quick Sort (desc)', quick_desc)]:
try:
time = timeit.timeit(lambda: func(data.copy()), number=10)
times[name] = time
print(f"{name}: {time:.6f}s")
except Exception as e:
print(f"Fehler bei {name}: {e}")
# Ausgabe der Ergebnisse
for name, time in sorted(times.items(), key=lambda x: x[1]):
print(f"{name}: {time:.6f}s")
11.2 Speicher- und Zeit-Optimierung mit partial
import timeit
from functools import partial
def memory_intensive_operation(data, operation, threshold=0):
"""Speicherintensive Operation mit Parametern"""
if operation == 'filter':
return [x for x in data if x > threshold]
elif operation == 'transform':
return [x * 2 for x in data if x > threshold]
return data
# Daten vorbereiten
large_data = list(range(10000))
# Partielle Funktionen für verschiedene Operationen
filter_op = partial(memory_intensive_operation, data=large_data, operation='filter', threshold=5000)
transform_op = partial(memory_intensive_operation, data=large_data, operation='transform', threshold=5000)
# Timing
filter_time = timeit.timeit(filter_op, number=1000)
transform_time = timeit.timeit(transform_op, number=1000)
print(f"Filter-Operation: {filter_time:.6f}s")
print(f"Transform-Operation: {transform_time:.6f}s")
# Mehrfachmessung für genauere Ergebnisse
filter_times = timeit.repeat(filter_op, number=100, repeat=5)
transform_times = timeit.repeat(transform_op, number=100, repeat=5)
print(f"Filter - Min: {min(filter_times):.6f}s, Avg: {sum(filter_times)/len(filter_times):.6f}s")
print(f"Transform - Min: {min(transform_times):.6f}s, Avg: {sum(transform_times)/len(transform_times):.6f}s")
12. Zusammenfassung
Das timeit-Modul ist ein leistungsstarkes Werkzeug für die Leistungsanalyse in Python:
- Hohe Präzision: Verwendet den besten verfügbaren Timer
- Wiederholungsmöglichkeiten: Mehrfachmessungen für genaue Ergebnisse
- Flexible Anpassung: Unterstützung für Setup-Code, verschiedene Wiederholungen
- Leistungssteigerung: Optimierte Ausführung für Benchmarking
- Einfache Integration: Einfaches Interface für Entwickler
Die Verwendung von functools.partial ist besonders wichtig, wenn man Funktionen mit Parametern in timeit messen möchte. Dies ermöglicht:
- Klare Trennung von Parametern und Zeitmessung
- Wiederverwendbare Timing-Funktionen
- Effizientere Benchmarking-Prozesse
- Bessere Lesbarkeit und Wartbarkeit des Codes
Die Kombination von timeit und functools.partial ist ein mächtiges Werkzeug für Entwickler, die präzise Performance-Messungen durchführen und optimierte Code-Strukturen entwickeln möchten.