yumeow-timer 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- yumeow_timer-0.1.0/LICENSE +21 -0
- yumeow_timer-0.1.0/PKG-INFO +114 -0
- yumeow_timer-0.1.0/README.md +88 -0
- yumeow_timer-0.1.0/pyproject.toml +41 -0
- yumeow_timer-0.1.0/setup.cfg +4 -0
- yumeow_timer-0.1.0/src/yumeow_timer/__init__.py +11 -0
- yumeow_timer-0.1.0/src/yumeow_timer/timing.py +281 -0
- yumeow_timer-0.1.0/src/yumeow_timer.egg-info/PKG-INFO +114 -0
- yumeow_timer-0.1.0/src/yumeow_timer.egg-info/SOURCES.txt +10 -0
- yumeow_timer-0.1.0/src/yumeow_timer.egg-info/dependency_links.txt +1 -0
- yumeow_timer-0.1.0/src/yumeow_timer.egg-info/requires.txt +2 -0
- yumeow_timer-0.1.0/src/yumeow_timer.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 YuMeow
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: yumeow-timer
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Lightweight timing utilities for performance diagnostics
|
|
5
|
+
Author-email: YuMeow <yumeow@example.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/yuzhTHU/nd2py
|
|
8
|
+
Project-URL: Repository, https://github.com/yuzhTHU/nd2py
|
|
9
|
+
Keywords: timer,timing,performance,profiling,benchmark
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: numpy>=1.20.0
|
|
24
|
+
Requires-Dist: pandas>=1.3.0
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
|
|
27
|
+
# yumeow-timer
|
|
28
|
+
|
|
29
|
+
Lightweight timing utilities for optional performance diagnostics.
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install yumeow-timer
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
### Basic Timer
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from yumeow_timer import Timer
|
|
43
|
+
|
|
44
|
+
timer = Timer()
|
|
45
|
+
for i in range(1000):
|
|
46
|
+
# do something
|
|
47
|
+
timer.add()
|
|
48
|
+
|
|
49
|
+
print(timer) # Timer(time=1.23 s, count=1.00 kiter, pace=1.23 ms/iter, speed=813 iter/s)
|
|
50
|
+
print(timer.to_str('pace')) # 1.23 ms/iter
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### NamedTimer (Multiple Categories)
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
from yumeow_timer import NamedTimer
|
|
57
|
+
|
|
58
|
+
timer = NamedTimer()
|
|
59
|
+
for i in range(1000):
|
|
60
|
+
if i % 2 == 0:
|
|
61
|
+
timer.add("even")
|
|
62
|
+
else:
|
|
63
|
+
timer.add("odd")
|
|
64
|
+
|
|
65
|
+
print(timer) # 1.23 ms/iter (even=1.23 ms/iter[50%]; odd=1.23 ms/iter[50%])
|
|
66
|
+
print(timer.to_str('pace', mode_of_detail='pace', mode_of_percent='by_count'))
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### ParallelTimer (Shared Time)
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
from yumeow_timer import ParallelTimer
|
|
73
|
+
|
|
74
|
+
timer = ParallelTimer()
|
|
75
|
+
for i in range(1000):
|
|
76
|
+
timer.add("process", n=1)
|
|
77
|
+
timer.add("render", n=1)
|
|
78
|
+
|
|
79
|
+
print(timer) # Shows shared time with separate counts
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Humanize Functions
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
from yumeow_timer import humanize_time, humanize_count, humanize_pace, humanize_speed
|
|
86
|
+
|
|
87
|
+
print(humanize_time(3661)) # 1.02 h
|
|
88
|
+
print(humanize_count(1500)) # 1.50 kiter
|
|
89
|
+
print(humanize_pace(0.00123)) # 1.23 ms/iter
|
|
90
|
+
print(humanize_speed(813)) # 813 iter/s
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## API Reference
|
|
94
|
+
|
|
95
|
+
### Timer
|
|
96
|
+
|
|
97
|
+
Basic timer that tracks time and count.
|
|
98
|
+
|
|
99
|
+
- `add(n=1, by="increment")` - Add count and elapsed time
|
|
100
|
+
- `clear(reset_last_add_time=False)` - Reset timer
|
|
101
|
+
- `to_str(mode="pace")` - Get human-readable string
|
|
102
|
+
- Properties: `time`, `count`, `pace`, `speed`
|
|
103
|
+
|
|
104
|
+
### NamedTimer
|
|
105
|
+
|
|
106
|
+
Extends Timer to track multiple named categories with separate time tracking.
|
|
107
|
+
|
|
108
|
+
### ParallelTimer
|
|
109
|
+
|
|
110
|
+
Extends Timer to track multiple named categories with shared time.
|
|
111
|
+
|
|
112
|
+
## License
|
|
113
|
+
|
|
114
|
+
MIT License - See LICENSE file for details.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# yumeow-timer
|
|
2
|
+
|
|
3
|
+
Lightweight timing utilities for optional performance diagnostics.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install yumeow-timer
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### Basic Timer
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from yumeow_timer import Timer
|
|
17
|
+
|
|
18
|
+
timer = Timer()
|
|
19
|
+
for i in range(1000):
|
|
20
|
+
# do something
|
|
21
|
+
timer.add()
|
|
22
|
+
|
|
23
|
+
print(timer) # Timer(time=1.23 s, count=1.00 kiter, pace=1.23 ms/iter, speed=813 iter/s)
|
|
24
|
+
print(timer.to_str('pace')) # 1.23 ms/iter
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### NamedTimer (Multiple Categories)
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
from yumeow_timer import NamedTimer
|
|
31
|
+
|
|
32
|
+
timer = NamedTimer()
|
|
33
|
+
for i in range(1000):
|
|
34
|
+
if i % 2 == 0:
|
|
35
|
+
timer.add("even")
|
|
36
|
+
else:
|
|
37
|
+
timer.add("odd")
|
|
38
|
+
|
|
39
|
+
print(timer) # 1.23 ms/iter (even=1.23 ms/iter[50%]; odd=1.23 ms/iter[50%])
|
|
40
|
+
print(timer.to_str('pace', mode_of_detail='pace', mode_of_percent='by_count'))
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### ParallelTimer (Shared Time)
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
from yumeow_timer import ParallelTimer
|
|
47
|
+
|
|
48
|
+
timer = ParallelTimer()
|
|
49
|
+
for i in range(1000):
|
|
50
|
+
timer.add("process", n=1)
|
|
51
|
+
timer.add("render", n=1)
|
|
52
|
+
|
|
53
|
+
print(timer) # Shows shared time with separate counts
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Humanize Functions
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from yumeow_timer import humanize_time, humanize_count, humanize_pace, humanize_speed
|
|
60
|
+
|
|
61
|
+
print(humanize_time(3661)) # 1.02 h
|
|
62
|
+
print(humanize_count(1500)) # 1.50 kiter
|
|
63
|
+
print(humanize_pace(0.00123)) # 1.23 ms/iter
|
|
64
|
+
print(humanize_speed(813)) # 813 iter/s
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## API Reference
|
|
68
|
+
|
|
69
|
+
### Timer
|
|
70
|
+
|
|
71
|
+
Basic timer that tracks time and count.
|
|
72
|
+
|
|
73
|
+
- `add(n=1, by="increment")` - Add count and elapsed time
|
|
74
|
+
- `clear(reset_last_add_time=False)` - Reset timer
|
|
75
|
+
- `to_str(mode="pace")` - Get human-readable string
|
|
76
|
+
- Properties: `time`, `count`, `pace`, `speed`
|
|
77
|
+
|
|
78
|
+
### NamedTimer
|
|
79
|
+
|
|
80
|
+
Extends Timer to track multiple named categories with separate time tracking.
|
|
81
|
+
|
|
82
|
+
### ParallelTimer
|
|
83
|
+
|
|
84
|
+
Extends Timer to track multiple named categories with shared time.
|
|
85
|
+
|
|
86
|
+
## License
|
|
87
|
+
|
|
88
|
+
MIT License - See LICENSE file for details.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "yumeow-timer"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Lightweight timing utilities for performance diagnostics"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {text = "MIT"}
|
|
11
|
+
authors = [
|
|
12
|
+
{name = "YuMeow", email = "yumeow@example.com"}
|
|
13
|
+
]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 3 - Alpha",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"Intended Audience :: Science/Research",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.10",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
25
|
+
]
|
|
26
|
+
keywords = ["timer", "timing", "performance", "profiling", "benchmark"]
|
|
27
|
+
requires-python = ">=3.10"
|
|
28
|
+
dependencies = [
|
|
29
|
+
"numpy>=1.20.0",
|
|
30
|
+
"pandas>=1.3.0",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.urls]
|
|
34
|
+
Homepage = "https://github.com/yuzhTHU/nd2py"
|
|
35
|
+
Repository = "https://github.com/yuzhTHU/nd2py"
|
|
36
|
+
|
|
37
|
+
[tool.setuptools.packages.find]
|
|
38
|
+
where = ["src"]
|
|
39
|
+
|
|
40
|
+
[tool.setuptools.package-dir]
|
|
41
|
+
"" = "src"
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""
|
|
2
|
+
yumeow-timer: Lightweight timing utilities for optional performance diagnostics.
|
|
3
|
+
|
|
4
|
+
Provides Timer, NamedTimer, and ParallelTimer classes for tracking
|
|
5
|
+
execution time and iteration counts with human-readable output.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .timing import Timer, NamedTimer, ParallelTimer
|
|
9
|
+
|
|
10
|
+
__version__ = "0.1.0"
|
|
11
|
+
__all__ = ["Timer", "NamedTimer", "ParallelTimer", "__version__"]
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Lightweight timing utilities for optional performance diagnostics.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import enum
|
|
6
|
+
import time
|
|
7
|
+
import numpy as np
|
|
8
|
+
import pandas as pd
|
|
9
|
+
from typing import Literal
|
|
10
|
+
|
|
11
|
+
__all__ = ["Timer", "NamedTimer", "ParallelTimer"]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
Mode = Literal["time", "count", "pace", "speed"]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def humanize_time(time):
|
|
18
|
+
"""Convert time in seconds to a human-readable string."""
|
|
19
|
+
if time == 0: return "0 s"
|
|
20
|
+
unit_scale = pd.Series({ # 需要保证从小到大排列
|
|
21
|
+
'μs': 1e-6, 'ms': 1e-3, 's': 1e0, 'min': 60, 'h': 3600, 'day': 86400
|
|
22
|
+
})
|
|
23
|
+
idx = max(0, unit_scale.searchsorted(time, side='right')-1)
|
|
24
|
+
time_unit, scale = unit_scale.index[idx], unit_scale.iloc[idx]
|
|
25
|
+
return f"{time / scale:.3g} {time_unit}"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def humanize_count(count, unit="iter"):
|
|
29
|
+
"""Convert count to a human-readable string."""
|
|
30
|
+
if count == 0: return f"0 {unit}"
|
|
31
|
+
unit_scale = pd.Series({ # 需要保证从小到大排列
|
|
32
|
+
f'n{unit}': 1e-9, f'μ{unit}': 1e-6, f'm{unit}': 1e-3,
|
|
33
|
+
f'{unit}': 1, f'k{unit}': 1e3, f'M{unit}': 1e6, f'G{unit}': 1e9
|
|
34
|
+
})
|
|
35
|
+
idx = max(0, unit_scale.searchsorted(count, side='right')-1)
|
|
36
|
+
count_unit, scale = unit_scale.index[idx], unit_scale.iloc[idx]
|
|
37
|
+
return f"{count / scale:.3g} {count_unit}"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def humanize_pace(pace, unit="iter"):
|
|
41
|
+
"""Convert pace (second/iter) to a human-readable string."""
|
|
42
|
+
if pace == 0: return f"0 s/{unit}"
|
|
43
|
+
unit_scale = pd.Series({ # 需要保证从小到大排列
|
|
44
|
+
f'μs/{unit}': 1e-6, f'ms/{unit}': 1e-3, f's/{unit}': 1e0,
|
|
45
|
+
f'min/{unit}': 60, f'h/{unit}': 3600, f'day/{unit}': 86400
|
|
46
|
+
})
|
|
47
|
+
idx = max(0, unit_scale.searchsorted(pace, side='right')-1)
|
|
48
|
+
pace_unit, scale = unit_scale.index[idx], unit_scale.iloc[idx]
|
|
49
|
+
return f"{pace / scale:.3g} {pace_unit}"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def humanize_speed(speed, unit="iter"):
|
|
53
|
+
"""Convert speed (iter/second) to a human-readable string."""
|
|
54
|
+
if speed == 0: return f"0 {unit}/s"
|
|
55
|
+
unit_scale = pd.Series({ # 需要保证从小到大排列
|
|
56
|
+
f'{unit}/day': 1/86400, f'{unit}/h': 1/3600, f'{unit}/min': 1/60, f'{unit}/s': 1,
|
|
57
|
+
f'k{unit}/s': 1e3, f'M{unit}/s': 1e6, f'G{unit}/s': 1e9
|
|
58
|
+
})
|
|
59
|
+
idx = max(0, unit_scale.searchsorted(speed, side='right')-1)
|
|
60
|
+
speed_unit, scale = unit_scale.index[idx], unit_scale.iloc[idx]
|
|
61
|
+
return f"{speed / scale:.3g} {speed_unit}"
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class Timer:
|
|
65
|
+
def __init__(self, unit="iter"):
|
|
66
|
+
""" 计时器 """
|
|
67
|
+
self._count = 0
|
|
68
|
+
self._time = 0
|
|
69
|
+
self.unit = unit
|
|
70
|
+
self.last_add_time = time.time()
|
|
71
|
+
|
|
72
|
+
def add(self, n=1, by: Literal["increment", "absolute"]="increment"):
|
|
73
|
+
if by == "increment": self._count += n
|
|
74
|
+
elif by == "absolute": self._count = n
|
|
75
|
+
self._time += (now := time.time()) - self.last_add_time
|
|
76
|
+
self.last_add_time = now
|
|
77
|
+
|
|
78
|
+
def clear(self, reset_last_add_time=False):
|
|
79
|
+
self._count = 0
|
|
80
|
+
self._time = 0
|
|
81
|
+
if reset_last_add_time:
|
|
82
|
+
self.last_add_time = time.time()
|
|
83
|
+
|
|
84
|
+
def to_str(self, mode: Mode = 'pace'):
|
|
85
|
+
if mode == 'pace': return humanize_pace(self.pace, unit=self.unit)
|
|
86
|
+
elif mode == "speed": return humanize_speed(self.speed, unit=self.unit)
|
|
87
|
+
elif mode == "count": return humanize_count(self.count, unit=self.unit)
|
|
88
|
+
elif mode == "time": return humanize_time(self.time)
|
|
89
|
+
else: raise ValueError(f"Unknown mode: {mode}. Supported modes are 'pace', 'speed', 'count', and 'time'.")
|
|
90
|
+
|
|
91
|
+
def __str__(self):
|
|
92
|
+
time = self.time_str()
|
|
93
|
+
count = self.count_str()
|
|
94
|
+
pace = self.pace_str()
|
|
95
|
+
speed = self.speed_str()
|
|
96
|
+
return f"{type(self).__name__}(time={time}, count={count}, pace={pace}, speed={speed})"
|
|
97
|
+
|
|
98
|
+
def __repr__(self):
|
|
99
|
+
return self.__str__()
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def time(self): return self._time
|
|
103
|
+
@property
|
|
104
|
+
def count(self): return self._count
|
|
105
|
+
@property
|
|
106
|
+
def pace(self): return self.time / self.count if self.count != 0 else 0
|
|
107
|
+
@property
|
|
108
|
+
def speed(self): return self.count / self.time if self.time != 0 else 0
|
|
109
|
+
def time_str(self): return humanize_time(self.time)
|
|
110
|
+
def count_str(self): return humanize_count(self.count, unit=self.unit)
|
|
111
|
+
def pace_str(self): return humanize_pace(self.pace, unit=self.unit)
|
|
112
|
+
def speed_str(self): return humanize_speed(self.speed, unit=self.unit)
|
|
113
|
+
|
|
114
|
+
def to_dict(self):
|
|
115
|
+
return {
|
|
116
|
+
'_count': self._count,
|
|
117
|
+
'_time': self._time,
|
|
118
|
+
'unit': self.unit,
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
@classmethod
|
|
122
|
+
def from_dict(cls, dict):
|
|
123
|
+
timer = cls(unit=dict['unit'])
|
|
124
|
+
timer._count = dict['_count']
|
|
125
|
+
timer._time = dict['_time']
|
|
126
|
+
return timer
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class NamedTimer(Timer):
|
|
130
|
+
def __init__(self, unit="iter"):
|
|
131
|
+
""" 对 Timer 的扩展,支持将 count 和 time 统计到不同的名称下 """
|
|
132
|
+
super().__init__(unit=unit)
|
|
133
|
+
self._count = {}
|
|
134
|
+
self._time = {}
|
|
135
|
+
|
|
136
|
+
def add(self, name, n=1, by: Literal["increment", "absolute"]="increment"):
|
|
137
|
+
if name and name not in self.names:
|
|
138
|
+
self._time[name] = self._count[name] = 0
|
|
139
|
+
if name is None: pass
|
|
140
|
+
elif by == "increment": self._count[name] += n
|
|
141
|
+
elif by == "absolute": self._count[name] = n
|
|
142
|
+
self._time[name] += (now := time.time()) - self.last_add_time
|
|
143
|
+
self.last_add_time = now
|
|
144
|
+
|
|
145
|
+
def clear(self, reset_last_add_time=False):
|
|
146
|
+
self._count = {}
|
|
147
|
+
self._time = {}
|
|
148
|
+
if reset_last_add_time:
|
|
149
|
+
self.last_add_time = time.time()
|
|
150
|
+
|
|
151
|
+
def to_str(
|
|
152
|
+
self,
|
|
153
|
+
mode: Mode='pace',
|
|
154
|
+
mode_of_detail: Mode|None='pace',
|
|
155
|
+
mode_of_percent: Literal['by_time', 'by_count']|None='by_time',
|
|
156
|
+
):
|
|
157
|
+
if mode == "time": total = humanize_time(self.time)
|
|
158
|
+
elif mode == "count": total = humanize_count(self.count, unit=self.unit)
|
|
159
|
+
elif mode == "pace": total = humanize_pace(self.pace, unit=self.unit)
|
|
160
|
+
elif mode == "speed": total = humanize_speed(self.speed, unit=self.unit)
|
|
161
|
+
else: raise ValueError(f"Unknown mode: {mode}. Supported modes are 'pace', 'speed', 'count', and 'time'.")
|
|
162
|
+
|
|
163
|
+
if mode_of_detail is None: detail = {k: "" for k in self.names}
|
|
164
|
+
elif mode_of_detail == "time": detail = {k: humanize_time(self.get_named_time(k)) for k in self.names}
|
|
165
|
+
elif mode_of_detail == "count": detail = {k: humanize_count(self.get_named_count(k), unit=self.unit) for k in self.names}
|
|
166
|
+
elif mode_of_detail == "pace": detail = {k: humanize_pace(self.get_named_pace(k), unit=self.unit) for k in self.names}
|
|
167
|
+
elif mode_of_detail == "speed": detail = {k: humanize_speed(self.get_named_speed(k), unit=self.unit) for k in self.names}
|
|
168
|
+
else: raise ValueError(f"Unknown mode_of_detail: {mode_of_detail}. Supported modes are 'pace', 'speed', 'count', and 'time'.")
|
|
169
|
+
|
|
170
|
+
if mode_of_percent is None: percent = {k: None for k in self.names}
|
|
171
|
+
elif mode_of_percent == 'by_time': percent = {k: self.get_named_time(k) / self.time if self.time > 0 else None for k in self.names}
|
|
172
|
+
elif mode_of_percent == 'by_count': percent = {k: self.get_named_count(k) / self.count if self.count > 0 else None for k in self.names}
|
|
173
|
+
else: raise ValueError(f"Unknown mode: {mode_of_percent}. Supported modes are 'by_time' and 'by_count'.")
|
|
174
|
+
|
|
175
|
+
detail_str = []
|
|
176
|
+
for k in sorted(self.names, key=percent.get, reverse=True):
|
|
177
|
+
if detail[k] and percent[k]:
|
|
178
|
+
detail_str.append(f"{k}={detail[k]}[{percent[k]:.0%}]")
|
|
179
|
+
elif detail[k]:
|
|
180
|
+
detail_str.append(f"{k}={detail[k]}")
|
|
181
|
+
elif percent[k]:
|
|
182
|
+
detail_str.append(f"{k}[{percent[k]:.0%}]")
|
|
183
|
+
else:
|
|
184
|
+
pass
|
|
185
|
+
detail_str = f" ({'; '.join(detail_str)})" if detail_str else ""
|
|
186
|
+
return f'{total}{detail_str}'
|
|
187
|
+
|
|
188
|
+
@property
|
|
189
|
+
def names(self): return list(self._time.keys())
|
|
190
|
+
@property
|
|
191
|
+
def time(self): return sum(self._time.values())
|
|
192
|
+
@property
|
|
193
|
+
def count(self): return sum(self._count.values())
|
|
194
|
+
def get_named_time(self, name): return self._time[name]
|
|
195
|
+
def get_named_count(self, name): return self._count[name]
|
|
196
|
+
def get_named_pace(self, name): return self._time[name] / self._count[name] if self._count[name] != 0 else 0
|
|
197
|
+
def get_named_speed(self, name): return self._count[name] / self._time[name] if self._time[name] != 0 else 0
|
|
198
|
+
@property
|
|
199
|
+
def named_time(self): return {k: self.get_named_time(k) for k in self.names}
|
|
200
|
+
@property
|
|
201
|
+
def named_count(self): return {k: self.get_named_count(k) for k in self.names}
|
|
202
|
+
@property
|
|
203
|
+
def named_pace(self): return {k: self.get_named_pace(k) for k in self.names}
|
|
204
|
+
@property
|
|
205
|
+
def named_speed(self): return {k: self.get_named_speed(k) for k in self.names}
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
class ParallelTimer(Timer):
|
|
209
|
+
def __init__(self, unit="iter"):
|
|
210
|
+
""" 对 Timer 的扩展,支持将 count 统计到不同的名称下,但 time 在各名称间共享 """
|
|
211
|
+
super().__init__(unit=unit)
|
|
212
|
+
self._count = {}
|
|
213
|
+
|
|
214
|
+
def add(self, name, n=1, by: Literal["increment", "absolute"]="increment"):
|
|
215
|
+
if name and name not in self.names:
|
|
216
|
+
self._count[name] = 0
|
|
217
|
+
if name is None: pass
|
|
218
|
+
elif by == "increment": self._count[name] += n
|
|
219
|
+
elif by == "absolute": self._count[name] = n
|
|
220
|
+
self._time += (now := time.time()) - self.last_add_time
|
|
221
|
+
self.last_add_time = now
|
|
222
|
+
|
|
223
|
+
def clear(self, reset_last_add_time=False):
|
|
224
|
+
self._count = {}
|
|
225
|
+
self._time = 0
|
|
226
|
+
if reset_last_add_time:
|
|
227
|
+
self.last_add_time = time.time()
|
|
228
|
+
|
|
229
|
+
def to_str(
|
|
230
|
+
self,
|
|
231
|
+
mode: Mode='pace',
|
|
232
|
+
mode_of_detail: Mode|None='pace',
|
|
233
|
+
mode_of_percent: Literal['by_time', 'by_count']|None='by_time'
|
|
234
|
+
):
|
|
235
|
+
if mode == "time": total = humanize_time(self.time)
|
|
236
|
+
elif mode == "count": total = humanize_count(self.count, unit=self.unit)
|
|
237
|
+
elif mode == "pace": total = humanize_pace(self.pace, unit=self.unit)
|
|
238
|
+
elif mode == "speed": total = humanize_speed(self.speed, unit=self.unit)
|
|
239
|
+
else: raise ValueError(f"Unknown mode: {mode}. Supported modes are 'pace', 'speed', 'count', and 'time'.")
|
|
240
|
+
|
|
241
|
+
if mode_of_detail is None: detail = {k: "" for k in self.names}
|
|
242
|
+
elif mode_of_detail == "time": detail = {k: humanize_time(self.get_named_time(k)) for k in self.names} # 不建议,因为 time 是共享的
|
|
243
|
+
elif mode_of_detail == "count": detail = {k: humanize_count(self.get_named_count(k), unit=self.unit) for k in self.names}
|
|
244
|
+
elif mode_of_detail == "pace": detail = {k: humanize_pace(self.get_named_pace(k), unit=self.unit) for k in self.names}
|
|
245
|
+
elif mode_of_detail == "speed": detail = {k: humanize_speed(self.get_named_speed(k), unit=self.unit) for k in self.names}
|
|
246
|
+
else: raise ValueError(f"Unknown mode_of_detail: {mode_of_detail}. Supported modes are 'pace', 'speed', 'count', and 'time'.")
|
|
247
|
+
|
|
248
|
+
if mode_of_percent is None: percent = {k: None for k in self.names}
|
|
249
|
+
elif mode_of_percent == 'by_time': percent = {k: self.get_named_time(k) / self.time if self.time > 0 else None for k in self.names} # 不建议,因为 time 是共享的
|
|
250
|
+
elif mode_of_percent == 'by_count': percent = {k: self.get_named_count(k) / self.count if self.count > 0 else None for k in self.names}
|
|
251
|
+
else: raise ValueError(f"Unknown mode: {mode_of_percent}. Supported modes are 'by_time' and 'by_count'.")
|
|
252
|
+
|
|
253
|
+
detail_str = []
|
|
254
|
+
for k in sorted(self.names, key=lambda x: percent.get(x) or -float('inf'), reverse=True):
|
|
255
|
+
if detail[k] and percent[k]:
|
|
256
|
+
detail_str.append(f"{k}={detail[k]}[{percent[k]:.0%}]")
|
|
257
|
+
elif detail[k]:
|
|
258
|
+
detail_str.append(f"{k}={detail[k]}")
|
|
259
|
+
elif percent[k]:
|
|
260
|
+
detail_str.append(f"{k}[{percent[k]:.0%}]")
|
|
261
|
+
else:
|
|
262
|
+
pass
|
|
263
|
+
detail_str = f" ({'; '.join(detail_str)})" if detail_str else ""
|
|
264
|
+
return f'{total}{detail_str}'
|
|
265
|
+
|
|
266
|
+
@property
|
|
267
|
+
def names(self): return list(self._count.keys())
|
|
268
|
+
@property
|
|
269
|
+
def count(self): return sum(self._count.values())
|
|
270
|
+
def get_named_time(self, name): return self._time
|
|
271
|
+
def get_named_count(self, name): return self._count[name]
|
|
272
|
+
def get_named_pace(self, name): return self._time / self._count[name] if self._count[name] != 0 else 0
|
|
273
|
+
def get_named_speed(self, name): return self._count[name] / self._time if self._time != 0 else 0
|
|
274
|
+
@property
|
|
275
|
+
def named_time(self): return {k: self.get_named_time(k) for k in self.names}
|
|
276
|
+
@property
|
|
277
|
+
def named_count(self): return {k: self.get_named_count(k) for k in self.names}
|
|
278
|
+
@property
|
|
279
|
+
def named_pace(self): return {k: self.get_named_pace(k) for k in self.names}
|
|
280
|
+
@property
|
|
281
|
+
def named_speed(self): return {k: self.get_named_speed(k) for k in self.names}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: yumeow-timer
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Lightweight timing utilities for performance diagnostics
|
|
5
|
+
Author-email: YuMeow <yumeow@example.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/yuzhTHU/nd2py
|
|
8
|
+
Project-URL: Repository, https://github.com/yuzhTHU/nd2py
|
|
9
|
+
Keywords: timer,timing,performance,profiling,benchmark
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: numpy>=1.20.0
|
|
24
|
+
Requires-Dist: pandas>=1.3.0
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
|
|
27
|
+
# yumeow-timer
|
|
28
|
+
|
|
29
|
+
Lightweight timing utilities for optional performance diagnostics.
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install yumeow-timer
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
### Basic Timer
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from yumeow_timer import Timer
|
|
43
|
+
|
|
44
|
+
timer = Timer()
|
|
45
|
+
for i in range(1000):
|
|
46
|
+
# do something
|
|
47
|
+
timer.add()
|
|
48
|
+
|
|
49
|
+
print(timer) # Timer(time=1.23 s, count=1.00 kiter, pace=1.23 ms/iter, speed=813 iter/s)
|
|
50
|
+
print(timer.to_str('pace')) # 1.23 ms/iter
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### NamedTimer (Multiple Categories)
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
from yumeow_timer import NamedTimer
|
|
57
|
+
|
|
58
|
+
timer = NamedTimer()
|
|
59
|
+
for i in range(1000):
|
|
60
|
+
if i % 2 == 0:
|
|
61
|
+
timer.add("even")
|
|
62
|
+
else:
|
|
63
|
+
timer.add("odd")
|
|
64
|
+
|
|
65
|
+
print(timer) # 1.23 ms/iter (even=1.23 ms/iter[50%]; odd=1.23 ms/iter[50%])
|
|
66
|
+
print(timer.to_str('pace', mode_of_detail='pace', mode_of_percent='by_count'))
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### ParallelTimer (Shared Time)
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
from yumeow_timer import ParallelTimer
|
|
73
|
+
|
|
74
|
+
timer = ParallelTimer()
|
|
75
|
+
for i in range(1000):
|
|
76
|
+
timer.add("process", n=1)
|
|
77
|
+
timer.add("render", n=1)
|
|
78
|
+
|
|
79
|
+
print(timer) # Shows shared time with separate counts
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Humanize Functions
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
from yumeow_timer import humanize_time, humanize_count, humanize_pace, humanize_speed
|
|
86
|
+
|
|
87
|
+
print(humanize_time(3661)) # 1.02 h
|
|
88
|
+
print(humanize_count(1500)) # 1.50 kiter
|
|
89
|
+
print(humanize_pace(0.00123)) # 1.23 ms/iter
|
|
90
|
+
print(humanize_speed(813)) # 813 iter/s
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## API Reference
|
|
94
|
+
|
|
95
|
+
### Timer
|
|
96
|
+
|
|
97
|
+
Basic timer that tracks time and count.
|
|
98
|
+
|
|
99
|
+
- `add(n=1, by="increment")` - Add count and elapsed time
|
|
100
|
+
- `clear(reset_last_add_time=False)` - Reset timer
|
|
101
|
+
- `to_str(mode="pace")` - Get human-readable string
|
|
102
|
+
- Properties: `time`, `count`, `pace`, `speed`
|
|
103
|
+
|
|
104
|
+
### NamedTimer
|
|
105
|
+
|
|
106
|
+
Extends Timer to track multiple named categories with separate time tracking.
|
|
107
|
+
|
|
108
|
+
### ParallelTimer
|
|
109
|
+
|
|
110
|
+
Extends Timer to track multiple named categories with shared time.
|
|
111
|
+
|
|
112
|
+
## License
|
|
113
|
+
|
|
114
|
+
MIT License - See LICENSE file for details.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/yumeow_timer/__init__.py
|
|
5
|
+
src/yumeow_timer/timing.py
|
|
6
|
+
src/yumeow_timer.egg-info/PKG-INFO
|
|
7
|
+
src/yumeow_timer.egg-info/SOURCES.txt
|
|
8
|
+
src/yumeow_timer.egg-info/dependency_links.txt
|
|
9
|
+
src/yumeow_timer.egg-info/requires.txt
|
|
10
|
+
src/yumeow_timer.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
yumeow_timer
|