blob: 69adeba3d6634ec19c09c4e7ec6b0821ecc93586 [file] [log] [blame]
Srikrishna Iyera8485b52022-08-01 13:29:12 -07001# Copyright lowRISC contributors.
2# Licensed under the Apache License, Version 2.0, see LICENSE for details.
3# SPDX-License-Identifier: Apache-2.0
4r"""An abstraction for maintaining job runtime and its units.
5"""
6
Andreas Kurthf2278bd2023-01-27 15:19:40 +00007from copy import copy
Srikrishna Iyera8485b52022-08-01 13:29:12 -07008from typing import Tuple
Andreas Kurthf2278bd2023-01-27 15:19:40 +00009import unittest
Srikrishna Iyera8485b52022-08-01 13:29:12 -070010
11
12class JobTime:
13 # Possible units.
14 units = ["h", "m", "s", "ms", "us", "ns", "ps", "fs"]
15 dividers = [60.0, ] * 3 + [1000.0, ] * 5
16
Andreas Kurthd15f28b2023-01-27 15:19:09 +000017 def __init__(self, time: float = 0.0, unit: str = "s", normalize: bool = True):
18 self.set(time, unit, normalize)
Srikrishna Iyera8485b52022-08-01 13:29:12 -070019
Andreas Kurthd15f28b2023-01-27 15:19:09 +000020 def set(self, time: float, unit: str, normalize: bool = True):
Srikrishna Iyera8485b52022-08-01 13:29:12 -070021 """Public API to set the instance variables time, unit."""
22 self.__time = time
23 self.__unit = unit
24 assert self.__unit in self.units
Andreas Kurthd15f28b2023-01-27 15:19:09 +000025 if normalize:
26 self._normalize()
Srikrishna Iyera8485b52022-08-01 13:29:12 -070027
28 def get(self) -> Tuple[float, str]:
29 """Returns the time and unit as a tuple."""
30 return self.__time, self.__unit
31
Andreas Kurthf2278bd2023-01-27 15:19:40 +000032 def with_unit(self, unit: str):
33 """Return a copy of this object that has a specific unit and a value
34 scaled accordingly.
35
36 Note that the scaling may not be lossless due to rounding errors and
37 limited precision.
38 """
39 target_index = self.units.index(unit)
40 index = self.units.index(self.__unit)
41 jt = copy(self)
42 while index < target_index:
43 index += 1
44 jt.__time *= self.dividers[index]
45 jt.__unit = self.units[index]
46 while index > target_index:
47 jt.__time /= self.dividers[index]
48 index -= 1
49 jt.__unit = self.units[index]
50 return jt
51
Srikrishna Iyera8485b52022-08-01 13:29:12 -070052 def _normalize(self):
53 """Brings the time and its units to a more meaningful magnitude.
54
55 If the time is very large with a lower magnitude, this method divides
56 the time to get it to the next higher magnitude recursively, stopping
57 if the next division causes the time to go < 1. Examples:
58 123123232ps -> 123.12us
59 23434s -> 6.509h
60
61 The supported magnitudes and their associated divider values are
62 provided by JobTime.units and JobTime.dividers.
63 """
64 if self.__time == 0:
65 return
66
67 index = self.units.index(self.__unit)
68 normalized_time = self.__time
69 while index > 0 and normalized_time >= self.dividers[index]:
70 normalized_time = normalized_time / self.dividers[index]
71 index = index - 1
72 self.__time = normalized_time
73 self.__unit = self.units[index]
74
75 def __str__(self):
76 """Indicates <time><unit> as string.
77
78 The time value is truncated to 3 decimal places.
79 Returns an empty string if the __time is set to 0.
80 """
81 if self.__time == 0:
82 return ""
83 else:
84 return f"{self.__time:.3f}{self.__unit}"
85
86 def __eq__(self, other) -> bool:
87 assert isinstance(other, JobTime)
88 other_time, other_unit = other.get()
89 return self.__unit == other_unit and self.__time == other_time
90
91 def __gt__(self, other) -> bool:
92 if self.__time == 0:
93 return False
94
95 assert isinstance(other, JobTime)
96 other_time, other_unit = other.get()
97 if other_time == 0:
98 return True
99
100 sidx = JobTime.units.index(self.__unit)
101 oidx = JobTime.units.index(other_unit)
102 if sidx < oidx:
103 return True
104 elif sidx > oidx:
105 return False
106 else:
107 return self.__time > other_time
Andreas Kurthf2278bd2023-01-27 15:19:40 +0000108
109
110class TestJobTimeMethods(unittest.TestCase):
111
112 def test_with_unit(self):
113 # First data set
114 h = JobTime(6, 'h', normalize=False)
115 m = JobTime(360, 'm', normalize=False)
116 s = JobTime(21600, 's', normalize=False)
117 ms = JobTime(21600000, 'ms', normalize=False)
118 for src in [h, m, s, ms]:
119 for unit, dst in [('h', h), ('m', m), ('s', s), ('ms', ms)]:
120 self.assertEqual(src.with_unit(unit), dst)
121 # Second data set
122 fs = JobTime(123456000000, 'fs', normalize=False)
123 ps = JobTime(123456000, 'ps', normalize=False)
124 ns = JobTime(123456, 'ns', normalize=False)
125 us = JobTime(123.456, 'us', normalize=False)
126 ms = JobTime(0.123456, 'ms', normalize=False)
127 for src in [fs, ps, ns, us, ms]:
128 for unit, dst in [('fs', fs), ('ps', ps), ('ns', ns), ('us', us),
129 ('ms', ms)]:
130 self.assertEqual(src.with_unit(unit), dst)