Reputation: 763
Suppose that I have a function:
def func(x):
time.sleep(x)
return x
It will be called in every place when needed, for example, func_a in class A
, func_b in class B
... And all the modules start from a main function, main()
.
Now I want to statistic the amount of the called freqs and time of func
. It's similar to profiler
, but I only want to get the statistics of func
; I do not care about other methods.
A Decorator can only get the time once, which is not sufficient for the total time. Another problem is that func
overrides a function in another package. It may be called by functions in the package, and it is not easy to add a decorator to every call method.
def time(func):
def wrapper(*args, **kwargs):
import time
st = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"execution time: {end - st}")
return result
return wrapper()
@time
def func(x):
...
Is there any simple method that I can use to get the total execution of func
with the minimum code?
Upvotes: 1
Views: 3520
Reputation: 1
I've made some improvements to the original code snippet by incorporating automatic data collection for decorated functions. This enhancement enables the creation of reports or the storage of collected statistics in MySQL for more in-depth analysis later on.
Let define the decorator as :
def static_vars(**kwargs):
def decorate(func):
for k in kwargs:
setattr(func, k, kwargs[k])
return func
return decorate
@static_vars(data={}, lock=threading.RLock())
def timing(f):
@wraps(f)
def wrap(*args, **kw):
ts = time.time()
result = f(*args, **kw)
te = time.time()
with timing.lock:
name = f.__module__ + '::' + f.__name__
fd = timing.data.get(name, {})
fd['count'] = fd.get('count', 0) + 1
fd['time'] = fd.get('time', 0) + te - ts
fd['last'] = te - ts
timing.data[name] = fd
return result
return wrap
Create a table to store the function execution statistics:
CREATE TABLE function_statistics (
id INT AUTO_INCREMENT PRIMARY KEY,
function_name VARCHAR(255) NOT NULL,
iteration INT NOT NULL DEFAULT '0',
call_count INT NOT NULL DEFAULT '0',
total_time DOUBLE(10,6) DEFAULT '0.000000',
average_time DOUBLE(10,6) GENERATED ALWAYS AS ((total_time / nullif(call_count,0))) VIRTUAL
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Upload the collected statistics to the database:
def upload_function_statitics(iteration, connection, cursor):
with timing.lock:
cursor.executemany(
f"""
INSERT INTO function_statistics
(function_name, iteration, call_count, total_time)
VALUES
(%s, %s, %s, %s)
""",
[
(k, iteration, v['count'], v['time'])
for k,v in timing.data.items()
]
)
connection.commit()
# reset counter
timing.data = {}
Apply the timing decorator to any function you want to measure:
@timing
def my_function():
# Your function code here
pass
Finally periodically call the upload_function_statistics to insert the collected statistics into the database (and reset underlying data).
Here is my full blog post in this topic, with additional SQL query to do some analysis : https://sleek-think.ovh/blog/89-python-creating-a-function-timing-decorator-and-uploading-statistics-to-mysql
Upvotes: 0
Reputation: 107015
If you simply want to have the total execution time printed next to the execution time of the current call, you can store the total execution time as a local variable in the decorator and add to it the execution time of each call:
import time
def timer(func):
def wrapper(*args, **kwargs):
nonlocal total
start = time.time()
result = func(*args, **kwargs)
duration = time.time() - start
total += duration
print(f"Execution time: {duration} Total: {total}")
return result
total = 0
return wrapper
Demo: Try it online!
Or if you want to be able to access the total execution time separately, you can expose it as an additional attribute of the wrapper function object:
import time
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
duration = time.time() - start
wrapper.total_time += duration
print(f"Execution time: {duration}")
return result
wrapper.total_time = 0
return wrapper
so that:
@timer
def func(x):
time.sleep(x)
return x
print(func(1))
print(func(2))
print(f'Total: {func.total_time}')
outputs:
Execution time: 1.00111985206604
1
Execution time: 2.0005435943603516
2
Total: 3.0016634464263916
Demo: Try it online!
Upvotes: 1