This assignment requires familiarity with the lecture materials presented in class through week 10.
You shall define a function named profile
in a module named call_profiler
. Calls to functions decorated with call_profiler.profile
will be profiled in a manner similar to that of cProfile
, i.e. the total number of calls to decorated functions during an interpreter session will be recorded, as will the cumulative amount of time spent executing each function. Profiling information shall be supplied via four other functions in the call_profile
module, as demonstrated below.
""" Provides decorator function "profile" that counts calls and cumulative execution time for all decorated functions for the duration of an interpreter session. """ __author__ = 'A student in CS 20P, someone@jeff.cis.cabrillo.edu' import time def profile(function): """ Decorates a function so that the number of calls and cumulative execution time of all calls can be reported by call_count() and cumulative_time(), respectively. Execution time is measured by calling time.perf_counter() before and after a call to the decorated function. """ pass # TODO def call_count(function): """ Returns the number of times a given function has been called during this interpreter session, assuming the function has been decorated by profile(). """ pass # TODO def call_counts(): """ Returns a dictionary mapping functions decorated by profile() to the number of times they have been called during this interpreter session. """ pass # TODO def cumulative_time(function): """ Returns the cumulative amount of time (in seconds) that have been spent executing calls to a given function during this interpreter session, assuming the function has been decorated by profile(). """ pass # TODO def cumulative_times(): """ Returns a dictionary mapping functions decorated by profile() to the cumulative amount of time (in seconds) that have been spent executing calls to a given function during this interpreter session. """ pass # TODO
Here is a module with a few functions from previous lectures, now decorated with call_profiler.profile
:
""" Various functions for experimenting with the call_profiler module. """ __author__ = 'Jeffrey Bergamini for CS 20P, jeffrey.bergamini@cabrillo.edu' import call_profiler @call_profiler.profile def dna(sequence) -> dict[str, int]: """ Counts the bases in a DNA string. :param sequence: A DNA string (expected to contain A/C/G/T characters). :return: A dict[str, int] with keys in bases 'ACGT', and associated base counts. """ return {base: sequence.count(base) for base in 'ACGT'} @call_profiler.profile def euler_004(): """ Single statement with walrus. """ return max(a * b for a in range(100, 1000) for b in range(100, 1000) if (prod_str := str(a * b)) == prod_str[::-1]) @call_profiler.profile def euler_009(): """ Generator expression passed to next()—we know there can be only one solution. """ return next(a * b * c for a in range(1, 334) for b in range(a + 1, 1000 - a) if a**2 + b**2 == (c := 1000 - a - b)**2)
And here is a REPL session demonstrating the profiling behavior:
>>> import call_profiler_demo >>> import call_profiler >>> call_profiler.call_counts() {<function dna at 0x1028d1da0>: 0, <function euler_004 at 0x1028d22a0>: 0, <function euler_009 at 0x1028d23e0>: 0} >>> call_profiler.call_count(call_profiler_demo.euler_004) 0 >>> call_profiler.cumulative_times() {<function dna at 0x1028d1da0>: 0.0, <function euler_004 at 0x1028d22a0>: 0.0, <function euler_009 at 0x1028d23e0>: 0.0} >>> call_profiler.cumulative_time(call_profiler_demo.euler_004) 0.0 >>> call_profiler_demo.euler_004() 906609 >>> call_profiler_demo.euler_004() 906609 >>> call_profiler_demo.euler_009() 31875000 >>> call_profiler_demo.dna('GATTACA') {'A': 3, 'C': 1, 'G': 1, 'T': 2} >>> call_profiler_demo.dna(open('/srv/datasets/chromosome4').read()) {'A': 3030352, 'C': 2102095, 'G': 2092163, 'T': 3009609} >>> call_profiler.call_counts() {<function dna at 0x1028d1da0>: 2, <function euler_004 at 0x1028d22a0>: 2, <function euler_009 at 0x1028d23e0>: 1} >>> call_profiler.call_count(call_profiler_demo.euler_004) 2 >>> call_profiler.cumulative_times() {<function dna at 0x1028d1da0>: 0.09427983299246989, <function euler_004 at 0x1028d22a0>: 0.20388466698932461, <function euler_009 at 0x1028d23e0>: 0.02965154201956466} >>> call_profiler.cumulative_time(call_profiler_demo.euler_004) 0.20388466698932461
Just for fun, as submissions are received, this table will be updated and ranked with regard to the number of lines in a fully functional solution, as measured by SLOCCount. Fewer lines isn't always better, but it's interesting to see by how much different approaches vary.
Rank | SLOC (lines) | User |
---|
Submit call_profiler.py
via turnin.
Feedback Robot
This project has a feedback robot that will run some tests on your submission and provide you with a feedback report via email within roughly one minute.
Please read the feedback carefully.
Due at 23:59:59 on the date listed on the syllabus.
Assignment 10
is worth 60 points.
Possible point values per category: --------------------------------------- call_counts() 15 call_count() 15 cumulative_times() 15 cumulative_time() 15 Possible deductions: Style and practices 10–20% Possible extra credit: Submission via Git 5% ---------------------------------------