← Python/Idiomatisch en performance
Les 6 van 8
MODULE 13 · 06◷ 30 min

Een herhaalbare meting opzetten met timeit

Na deze les kun je
  • Totale meettijd van tijd per uitvoering onderscheiden.
  • Echte Timer-metingen met vaste aantallen herhalen.
  • Twee callables met dezelfde meetinstellingen vergelijken zonder vaste snelheidsclaim.

Je verandert een lus en ziet één keer een kleinere klokwaarde. Was je code beter, of was de computer op dat moment minder druk? Een bruikbare performancekeuze begint met dezelfde taak, meerdere metingen en een duidelijk beschreven meeteenheid.

Wat meet je precies?

timeit meet kleine stukken Pythoncode met herhaling. Een Timer kan een callable ontvangen, zodat je geen dynamische bronstring hoeft samen te stellen. De callable wordt tijdens de meting werkelijk aangeroepen.

Het argument number is het aantal uitvoeringen binnen één meting. repeat bepaalt hoeveel van zulke metingen je maakt. Bij number=4 en repeat=3 wordt de callable dus twaalf keer uitgevoerd en ontvang je drie totale tijden in seconden.

Een totale tijd van 0.06 seconden voor drie uitvoeringen betekent 0.02 seconden per uitvoering. De deling door number hoort bij de interpretatie, niet bij de timerinstelling. Houd seconden, milliseconden en aantallen zichtbaar uit elkaar.

Een echte timer, zonder een tijdwaarde te voorspellen
from timeit import Timer

calls = []
def work():
    calls.append(1)
    return sum([3, 5, 8])

samples = Timer(work).repeat(number=4, repeat=3)
print(len(calls))
print(len(samples))
print(all(type(value) is float and value >= 0 for value in samples))
Verwachte uitvoer
12
3
True

Het voorbeeld print geen toevallig tijdcijfer dat op iedere computer anders is. Het laat zien dat de echte bewerking twaalf keer is uitgevoerd en dat er drie niet-negatieve tijdmetingen zijn. De tijdwaarden bestaan wel degelijk; ze worden alleen niet als vaste uitkomst voorspeld.

De omstandigheden benoemen

Dezelfde invoer, dezelfde herhalingsaantallen en hetzelfde werk zijn voorwaarden voor een zinvolle vergelijking. Een meting die de ene keer ook bestandsinvoer of voorbereiding omvat, beantwoordt een andere vraag. Beslis daarom vooraf wat binnen de callable hoort.

timeit schakelt tijdens het meten standaard de garbage collector tijdelijk uit. Dat kan helpen om kleine bewerkingen te vergelijken, maar betekent ook dat een meting niet alle omstandigheden van een langlopend programma nabootst. Een sandboxmeting is bewijs over die proef, geen garantie voor iedere productieserver.

Een beste waarde voorzichtig lezen

Van herhaalde metingen is de kleinste waarde een bruikbare schatting van de minst verstoorde uitvoering. Bewaar daarnaast de andere waarden: een grote spreiding kan reden zijn om de proef te onderzoeken. Alleen de gunstigste waarde tonen verbergt die context.

De oefening berekent de kleinste totale tijd gedeeld door number. Zij kiest geen willekeurige snelheidsgrens en belooft niet welke variant wint. Een tijdverschil krijgt pas betekenis nadat beide varianten aantoonbaar hetzelfde resultaat maken.

Veelgemaakte fout: appels met herhalingen vergelijken

Je meet de oude functie honderd keer en de nieuwe duizend keer. De nieuwe totale tijd is groter, maar daaruit volgt nog niet dat één aanroep trager is. Deel door het eigen aantal of gebruik meteen gelijke instellingen.

Rekenvoorbeeld: totale tijden zijn geen tijden per aanroep
old_total, old_number = 12, 100
new_total, new_number = 60, 1000
print(old_total / old_number)
print(new_total / new_number)
print(new_total / new_number < old_total / old_number)
Verwachte uitvoer
0.12
0.06
True

Hier zijn de tijden uitsluitend rekenvoorbeelden om de eenheid uit te leggen, geen gemeten snelheidsresultaten. Het echte meten gebeurt in het voorgaande voorbeeld en in de opdrachten. Eenheden vergelijken voorkomt een fout voordat je een ingewikkelder benchmark bouwt.

Een controleerbare meetfunctie

De meegeleverde timer_factory maakt een echte timeit.Timer. Laat die de callable ontvangen en roep zijn repeat-methode aan met de gevraagde instellingen. De controles tellen de werkelijke callable-aanroepen en bewaren de echte meetwaarden; ze vervangen de klok niet door succesuitvoer.

De aantallen zijn in de meetopdrachten positieve gehele getallen. Voor de twee rekenhulpen controleer je zelf expliciet op een positief aantal; een lege lijst met metingen heeft bovendien geen beste waarde. Onzinnige invoer krijgt de afgesproken ValueError.

AAN DE SLAG 0/4
1

Seconden per uitvoering berekenen

Vul per_call(total_seconds, number) in: geef totale seconden gedeeld door het aantal als float terug. Bij number<=0 geef je ValueError("aantal moet positief zijn").

2

De echte Timer laten herhalen

Vul measure(fn, *, number, repeat, timer_factory=Timer) in: maak precies één timer_factory(fn) en geef diens echte repeat(number=number, repeat=repeat)-lijst ongewijzigd terug. Number en repeat zijn positieve gehele getallen.

3

De beste tijd normaliseren

Vul best_per_call(samples, number) in: geef min(samples) / number als float. Bij number<=0 volgt ValueError("aantal moet positief zijn"); anders geeft lege samples ValueError("metingen ontbreken").

4

Beide varianten gelijk behandelen

Vul compare(left, right, *, number, repeat, timer_factory=Timer) in: meet beide precies eenmaal met measure en dezelfde instellingen. Geef alleen de dictionary met keys left en right en de ongewijzigde ruwe meetlijsten terug. Number/repeat zijn positief.

Samenvatting

  • number telt uitvoeringen per meting; repeat telt metingen.
  • Vergelijk dezelfde taak en eenheid, met behoud van de ruwe meetwaarden.
  • Werkelijke timings variëren; correct gedrag en juiste meetopzet blijven apart toetsbaar.

Hierna onderzoek je hoe een cache rekenwerk kan besparen en tegelijk objecten in het geheugen houdt.

timing_tools.py
UitvoerPython
›
›_

Hier komt je resultaat.

Voer je code uit en ontdek
wat je hebt gemaakt.

0/4 stappen
EVEN MEEKIJKEN

Bekijk hoe het kan.

Je mag de oplossing bekijken. Deze poging telt dan niet mee als afgeronde les. Met Reset kun je daarna opnieuw zelf oefenen.