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").
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.
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.
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))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.
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.
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.
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.
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)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.
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.
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").
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.
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").
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.
number telt uitvoeringen per meting; repeat telt metingen.Hierna onderzoek je hoe een cache rekenwerk kan besparen en tegelijk objecten in het geheugen houdt.
Voer je code uit en ontdek
wat je hebt gemaakt.
Je hoeft het niet in één keer goed te doen. Na het uitvoeren zie je hier welke stappen al kloppen.