Skip to content

Commit dd22185

Browse files
committed
FEAT: added timings to debug log
1 parent 1cdb0f9 commit dd22185

File tree

2 files changed

+96
-1
lines changed

2 files changed

+96
-1
lines changed

larray_editor/arrayadapter.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@
5050
from larray.util.misc import Product
5151

5252
from larray_editor.utils import (get_sample, scale_to_01range,
53-
is_number_value_vectorized, logger)
53+
is_number_value_vectorized, logger,
54+
timed)
5455
from larray_editor.commands import CellValueChange
5556

5657

@@ -397,6 +398,7 @@ def close(self):
397398
# TODO: factorize with LArrayArrayAdapter (so that we get the attributes
398399
# handling of LArrayArrayAdapter for all types and the larray adapter
399400
# can benefit from the generic code here
401+
@timed(logger)
400402
def get_data_values_and_attributes(self, h_start, v_start, h_stop, v_stop):
401403
"""h_stop and v_stop should *not* be included"""
402404
# TODO: implement region caching
@@ -551,6 +553,7 @@ def get_sample(self):
551553
# return self.get_values(0, 0, min(width, 20), min(height, 20))
552554
return self.get_data_values_and_attributes(0, 0, min(width, 20), min(height, 20))['values']
553555

556+
@timed(logger)
554557
def update_finite_min_max_values(self, finite_values: np.ndarray,
555558
h_start: int, v_start: int,
556559
h_stop: int, v_stop: int):
@@ -652,6 +655,7 @@ def can_sort_hlabel(self, row_idx, col_idx) -> bool:
652655
def sort_hlabel(self, row_idx, col_idx, ascending):
653656
pass
654657

658+
@timed(logger)
655659
def get_vlabels(self, start, stop) -> dict:
656660
chunk_values = self.get_vlabels_values(start, stop)
657661
if isinstance(chunk_values, list) and len(chunk_values) == 0:
@@ -667,6 +671,7 @@ def get_vlabels_values(self, start, stop):
667671
# that would be slower than what we have now.
668672
return [[i] for i in range(start, stop)]
669673

674+
@timed(logger)
670675
def get_hlabels(self, start, stop):
671676
values = self.get_hlabels_values(start, stop)
672677
return {'values': values, 'decoration': self.get_hlabels_decorations(start, stop, values)}
@@ -899,6 +904,7 @@ def __init__(self, data, attributes=None):
899904
self.vmin = {}
900905
self.vmax = {}
901906

907+
@timed(logger)
902908
def update_finite_min_max_values(self, finite_values: np.ndarray,
903909
h_start: int, v_start: int,
904910
h_stop: int, v_stop: int):

larray_editor/utils.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import sys
88
import math
99
import logging
10+
import time
1011
import traceback
1112
from contextlib import contextmanager
1213
from pathlib import Path
@@ -806,3 +807,91 @@ def get_func_name(frame):
806807
# defined, not the instance class which is usually much more useful
807808
func_name = f"{frame.f_locals['self'].__class__.__name__}.{func_name}"
808809
return func_name
810+
811+
812+
def time2str(seconds, precision="auto"):
813+
"""Format a duration in seconds as a string using given precision.
814+
815+
Parameters
816+
----------
817+
seconds : float
818+
Duration (in seconds) to format.
819+
precision : str, optional
820+
Precision of the output. Defaults to "auto" (the largest unit minus 2).
821+
See examples below.
822+
823+
Returns
824+
-------
825+
str
826+
# FIXME: round values instead of truncating them
827+
Examples
828+
--------
829+
>>> time2str(3727.2785, precision="ns")
830+
'1 hour 2 minutes 7 seconds 278 ms 500 µs'
831+
>>> # auto: the largest unit is hour, the unit two steps below is seconds => precision = seconds
832+
>>> time2str(3727.2785)
833+
'1 hour 2 minutes 7 seconds'
834+
>>> time2str(3605.2785)
835+
'1 hour 5 seconds'
836+
>>> time2str(3727.2785, precision="hour")
837+
'1 hour'
838+
>>> time2str(3723.1234567890123456789, precision="ns")
839+
'1 hour 2 minutes 3 seconds 123 ms 456 µs 789 ns'
840+
>>> time2str(3723.1234567890123456789)
841+
'1 hour 2 minutes 3 seconds'
842+
"""
843+
# for Python 3.7+, we could use a dict (and rely on dict ordering)
844+
divisors = [
845+
('ns', 1000),
846+
('µs', 1000),
847+
('ms', 1000),
848+
('second', 60),
849+
('minute', 60),
850+
('hour', 24),
851+
('day', 365),
852+
]
853+
precision_map = {
854+
'day': 6,
855+
'hour': 5,
856+
'minute': 4,
857+
'second': 3,
858+
'ms': 2,
859+
'µs': 1,
860+
'ns': 0,
861+
}
862+
863+
values = []
864+
str_parts = []
865+
ns = int(seconds * 10 ** 9)
866+
value = ns
867+
for cur_precision, (unit, divisor_for_next) in enumerate(divisors):
868+
next_value, cur_value = divmod(value, divisor_for_next)
869+
values.append(cur_value)
870+
if next_value == 0:
871+
break
872+
value = next_value
873+
max_prec = len(values) - 1
874+
int_precision = max_prec - 2 if precision == 'auto' else precision_map[precision]
875+
for cur_precision, (cur_value, (unit, divisor_for_next)) in enumerate(zip(values, divisors)):
876+
if cur_value > 0 and cur_precision >= int_precision:
877+
str_parts.append(f"{cur_value:d} {unit}{'s' if cur_value > 1 and cur_precision > 2 else ''}")
878+
return ' '.join(str_parts[::-1])
879+
880+
881+
def timed(logger):
882+
def decorator(func):
883+
def new_func(*args, **kwargs):
884+
# testing for this outside new_func to make the decorator return
885+
# the original func if the logger is not enabled does not work
886+
# because the logger is not configured yet when the code to be
887+
# profiled is defined (and the decorator is called)
888+
if logger.isEnabledFor(logging.DEBUG):
889+
start = time.perf_counter()
890+
res = func(*args, **kwargs)
891+
time_taken = time.perf_counter() - start
892+
logger.debug(f"{func.__name__} done in {time2str(time_taken)}")
893+
return res
894+
else:
895+
return func(*args, **kwargs)
896+
return new_func
897+
return decorator

0 commit comments

Comments
 (0)