I'm doing a helper class to make benchmarking loops easier, which consists on timing it's entire execution and each user defined amount of iterations
:
import time
class LoopTimer:
@staticmethod
def run(fn, start, end, step = 1, batch_size = 1, precision = 8,
template = 'iteration = {current_iteration} \t took {batch_elapsed_time:.8f}s \t total = {total_elapsed_time:.8f}s \t value = {value}'):
counter, counter_total = time.process_time(), time.process_time()
for i in range(start, end, step):
value = fn(i)
if i > 0 & i % batch_size == 0:
end = time.process_time()
print(template.format_map({
'current_iteration':i,
'batch_elapsed_time': end-counter,
'total_elapsed_time': end-counter_total,
'value': value,
'batch_start': counter,
'batch_end': end,
'loop_start': counter_total
}))
counter = time.process_time()
How can users be able to define LoopTimer.precision
so there's no need to create a new template to, for example, limit the current float precision (8)? Of course, it could be doable by preformatting the string like:
template = 'iteration = {current_iteration} \t took {batch_elapsed_time:.*precision1*f}s \t total = {total_elapsed_time:.*precision2*f}s \t value = '
print(template.replace('*precision1*', '1').replace('*precision2*', '9').format_map({
'current_iteration':i,
'batch_elapsed_time': end-counter,
'total_elapsed_time': end-counter_total,
'value': value,
'batch_start': counter,
'batch_end': end,
'loop_start': counter_total
}))
But feels a bit dirty after seeing str.format
capabilities.
There's also a strange behaviour when formatting (using the template defined above):
iteration = 97 took 0.00000000s total = 0.00000000s value = 158456325028528675187087900672
iteration = 98 took 0.00000000s total = 0.00000000s value = 316912650057057350374175801344
iteration = 99 took 0.00000000s total = 0.00000000s value = 633825300114114700748351602688
iteration = 100 took 0.00000000s total = 0.00000000s value = 1267650600228229401496703205376
iteration = 101 took 0.00000000s total = 0.00000000s value = 2535301200456458802993406410752
iteration = 102 took 0.00000000s total = 0.00000000s value = 5070602400912917605986812821504
How could this be fixed?
One level of nested format is allowed so you can change the template to: template = 'iteration = {current_iteration} took {batch_elapsed_time:.{precision}f}s total = {total_elapsed_time:.{precision}f}s value = {value}'
and then add 'precision': precision
in the format_map()
.
\t
length is not fixed, it insert spaces to make the current substring 8 characters wide, then go to the next substring. For example, the output of print("1\t23\t456\t7890")
is
1 23 456 7890
, 1
is followed by 7 spaces \t
, 23
is followed by 6 spaces \t
, 456
is followed by 5 spaces \t
, each substring are 8 characters wide. Therefore, I used fixed 6 spaces is used in template.
Test run for LoopTimer.run(lambda i: 2 ** i, 97, 103, precision = 4)
:
iteration = 97 took 0.0000s total = 0.0000s value = 158456325028528675187087900672
iteration = 98 took 0.0000s total = 0.0000s value = 316912650057057350374175801344
iteration = 99 took 0.0000s total = 0.0000s value = 633825300114114700748351602688
iteration = 100 took 0.0000s total = 0.0000s value = 1267650600228229401496703205376
iteration = 101 took 0.0000s total = 0.0000s value = 2535301200456458802993406410752
iteration = 102 took 0.0000s total = 0.0000s value = 5070602400912917605986812821504