speedopy 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.
- speedopy-0.1.0/LICENSE +21 -0
- speedopy-0.1.0/PKG-INFO +98 -0
- speedopy-0.1.0/README.md +71 -0
- speedopy-0.1.0/pyproject.toml +37 -0
- speedopy-0.1.0/setup.cfg +4 -0
- speedopy-0.1.0/speedopy/__init__.py +36 -0
- speedopy-0.1.0/speedopy/decorators.py +200 -0
- speedopy-0.1.0/speedopy.egg-info/PKG-INFO +98 -0
- speedopy-0.1.0/speedopy.egg-info/SOURCES.txt +9 -0
- speedopy-0.1.0/speedopy.egg-info/dependency_links.txt +1 -0
- speedopy-0.1.0/speedopy.egg-info/top_level.txt +1 -0
speedopy-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 speedopy contributors
|
|
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.
|
speedopy-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: speedopy
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Zero-dependency performance measurement decorators for Python
|
|
5
|
+
Author-email: AayushtheCoder01 <aayushkumarkumar1234@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/AayushtheCoder01/speedopy
|
|
8
|
+
Project-URL: Repository, https://github.com/AayushtheCoder01/speedopy
|
|
9
|
+
Project-URL: Issues, https://github.com/AayushtheCoder01/speedopy/issues
|
|
10
|
+
Keywords: performance,profiling,benchmark,decorator,timing,memory
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Software Development :: Testing
|
|
22
|
+
Classifier: Topic :: System :: Benchmark
|
|
23
|
+
Requires-Python: >=3.8
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# speedopy
|
|
29
|
+
|
|
30
|
+
Zero-dependency performance measurement decorators for Python.
|
|
31
|
+
|
|
32
|
+
Just import a decorator, slap it on your function, and instantly see metrics printed to the console. No config, no setup, no external packages.
|
|
33
|
+
|
|
34
|
+
## Install
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install speedopy
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Decorators
|
|
41
|
+
|
|
42
|
+
| Decorator | What It Measures |
|
|
43
|
+
|---|---|
|
|
44
|
+
| `cpu_time` | CPU processing time (excludes I/O) |
|
|
45
|
+
| `real_time` | Wall-clock time |
|
|
46
|
+
| `memory_usage` | Peak memory allocated (auto-formats B/KB/MB) |
|
|
47
|
+
| `thread_time` | CPU time for the current thread only |
|
|
48
|
+
| `call_count` | Number of times a function has been called |
|
|
49
|
+
| `function_profile` | Full cProfile report (top 10 calls) |
|
|
50
|
+
| `io_time` | I/O wait time (real - cpu) |
|
|
51
|
+
| `gc_stats` | Garbage collections triggered |
|
|
52
|
+
| `object_count` | Net new Python objects created |
|
|
53
|
+
|
|
54
|
+
## Usage
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
from speedopy import cpu_time, memory_usage, io_time
|
|
58
|
+
|
|
59
|
+
@cpu_time
|
|
60
|
+
@memory_usage
|
|
61
|
+
@io_time
|
|
62
|
+
def process_data():
|
|
63
|
+
data = [i ** 2 for i in range(1_000_000)]
|
|
64
|
+
return sum(data)
|
|
65
|
+
|
|
66
|
+
process_data()
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Output:
|
|
70
|
+
```
|
|
71
|
+
[io_time] process_data -> real: 0.182345s, cpu: 0.171875s, io_wait: 0.010470s
|
|
72
|
+
[memory_usage] process_data -> current: 472 B, peak: 8.06 MB
|
|
73
|
+
[cpu_time] process_data -> 0.183291s
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Stack Multiple Decorators
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from speedopy import cpu_time, memory_usage, call_count
|
|
80
|
+
|
|
81
|
+
@call_count
|
|
82
|
+
@cpu_time
|
|
83
|
+
@memory_usage
|
|
84
|
+
def my_func():
|
|
85
|
+
return sum(range(500_000))
|
|
86
|
+
|
|
87
|
+
my_func() # [call_count] my_func -> called 1 time(s)
|
|
88
|
+
my_func() # [call_count] my_func -> called 2 time(s)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Requirements
|
|
92
|
+
|
|
93
|
+
- Python 3.8+
|
|
94
|
+
- No external dependencies
|
|
95
|
+
|
|
96
|
+
## License
|
|
97
|
+
|
|
98
|
+
MIT
|
speedopy-0.1.0/README.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# speedopy
|
|
2
|
+
|
|
3
|
+
Zero-dependency performance measurement decorators for Python.
|
|
4
|
+
|
|
5
|
+
Just import a decorator, slap it on your function, and instantly see metrics printed to the console. No config, no setup, no external packages.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install speedopy
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Decorators
|
|
14
|
+
|
|
15
|
+
| Decorator | What It Measures |
|
|
16
|
+
|---|---|
|
|
17
|
+
| `cpu_time` | CPU processing time (excludes I/O) |
|
|
18
|
+
| `real_time` | Wall-clock time |
|
|
19
|
+
| `memory_usage` | Peak memory allocated (auto-formats B/KB/MB) |
|
|
20
|
+
| `thread_time` | CPU time for the current thread only |
|
|
21
|
+
| `call_count` | Number of times a function has been called |
|
|
22
|
+
| `function_profile` | Full cProfile report (top 10 calls) |
|
|
23
|
+
| `io_time` | I/O wait time (real - cpu) |
|
|
24
|
+
| `gc_stats` | Garbage collections triggered |
|
|
25
|
+
| `object_count` | Net new Python objects created |
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
from speedopy import cpu_time, memory_usage, io_time
|
|
31
|
+
|
|
32
|
+
@cpu_time
|
|
33
|
+
@memory_usage
|
|
34
|
+
@io_time
|
|
35
|
+
def process_data():
|
|
36
|
+
data = [i ** 2 for i in range(1_000_000)]
|
|
37
|
+
return sum(data)
|
|
38
|
+
|
|
39
|
+
process_data()
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Output:
|
|
43
|
+
```
|
|
44
|
+
[io_time] process_data -> real: 0.182345s, cpu: 0.171875s, io_wait: 0.010470s
|
|
45
|
+
[memory_usage] process_data -> current: 472 B, peak: 8.06 MB
|
|
46
|
+
[cpu_time] process_data -> 0.183291s
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Stack Multiple Decorators
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from speedopy import cpu_time, memory_usage, call_count
|
|
53
|
+
|
|
54
|
+
@call_count
|
|
55
|
+
@cpu_time
|
|
56
|
+
@memory_usage
|
|
57
|
+
def my_func():
|
|
58
|
+
return sum(range(500_000))
|
|
59
|
+
|
|
60
|
+
my_func() # [call_count] my_func -> called 1 time(s)
|
|
61
|
+
my_func() # [call_count] my_func -> called 2 time(s)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Requirements
|
|
65
|
+
|
|
66
|
+
- Python 3.8+
|
|
67
|
+
- No external dependencies
|
|
68
|
+
|
|
69
|
+
## License
|
|
70
|
+
|
|
71
|
+
MIT
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "speedopy"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Zero-dependency performance measurement decorators for Python"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {text = "MIT"}
|
|
11
|
+
requires-python = ">=3.8"
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "AayushtheCoder01", email = "aayushkumarkumar1234@gmail.com"},
|
|
14
|
+
]
|
|
15
|
+
keywords = ["performance", "profiling", "benchmark", "decorator", "timing", "memory"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.8",
|
|
22
|
+
"Programming Language :: Python :: 3.9",
|
|
23
|
+
"Programming Language :: Python :: 3.10",
|
|
24
|
+
"Programming Language :: Python :: 3.11",
|
|
25
|
+
"Programming Language :: Python :: 3.12",
|
|
26
|
+
"Programming Language :: Python :: 3.13",
|
|
27
|
+
"Topic :: Software Development :: Testing",
|
|
28
|
+
"Topic :: System :: Benchmark",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[project.urls]
|
|
32
|
+
Homepage = "https://github.com/AayushtheCoder01/speedopy"
|
|
33
|
+
Repository = "https://github.com/AayushtheCoder01/speedopy"
|
|
34
|
+
Issues = "https://github.com/AayushtheCoder01/speedopy/issues"
|
|
35
|
+
|
|
36
|
+
[tool.setuptools.packages.find]
|
|
37
|
+
include = ["speedopy*"]
|
speedopy-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""
|
|
2
|
+
speedopy — A collection of zero-dependency performance measurement decorators.
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
from speedopy import cpu_time, real_time, memory_usage
|
|
6
|
+
|
|
7
|
+
@cpu_time
|
|
8
|
+
def my_function():
|
|
9
|
+
...
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
__version__ = "0.1.0"
|
|
13
|
+
|
|
14
|
+
from .decorators import (
|
|
15
|
+
cpu_time,
|
|
16
|
+
real_time,
|
|
17
|
+
memory_usage,
|
|
18
|
+
thread_time,
|
|
19
|
+
call_count,
|
|
20
|
+
function_profile,
|
|
21
|
+
io_time,
|
|
22
|
+
gc_stats,
|
|
23
|
+
object_count,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
"cpu_time",
|
|
28
|
+
"real_time",
|
|
29
|
+
"memory_usage",
|
|
30
|
+
"thread_time",
|
|
31
|
+
"call_count",
|
|
32
|
+
"function_profile",
|
|
33
|
+
"io_time",
|
|
34
|
+
"gc_stats",
|
|
35
|
+
"object_count",
|
|
36
|
+
]
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import time
|
|
2
|
+
import tracemalloc
|
|
3
|
+
import cProfile
|
|
4
|
+
import pstats
|
|
5
|
+
import io
|
|
6
|
+
import gc
|
|
7
|
+
from functools import wraps
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# ─────────────────────────────────────────────
|
|
11
|
+
# 1. CPU Time
|
|
12
|
+
# ─────────────────────────────────────────────
|
|
13
|
+
def cpu_time(func):
|
|
14
|
+
"""Measures CPU processing time (excludes I/O wait, sleep, etc.)."""
|
|
15
|
+
@wraps(func)
|
|
16
|
+
def wrapper(*args, **kwargs):
|
|
17
|
+
start = time.process_time_ns()
|
|
18
|
+
result = func(*args, **kwargs)
|
|
19
|
+
elapsed_ns = time.process_time_ns() - start
|
|
20
|
+
elapsed = elapsed_ns / 1_000_000_000
|
|
21
|
+
print(f"[cpu_time] {func.__name__} -> {elapsed:.6f}s")
|
|
22
|
+
return result
|
|
23
|
+
return wrapper
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# ─────────────────────────────────────────────
|
|
27
|
+
# 2. Real (Wall-Clock) Time
|
|
28
|
+
# ─────────────────────────────────────────────
|
|
29
|
+
def real_time(func):
|
|
30
|
+
"""Measures wall-clock (real) time including I/O, sleep, etc."""
|
|
31
|
+
@wraps(func)
|
|
32
|
+
def wrapper(*args, **kwargs):
|
|
33
|
+
start = time.perf_counter()
|
|
34
|
+
result = func(*args, **kwargs)
|
|
35
|
+
elapsed = time.perf_counter() - start
|
|
36
|
+
print(f"[real_time] {func.__name__} -> {elapsed:.6f}s")
|
|
37
|
+
return result
|
|
38
|
+
return wrapper
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# ─────────────────────────────────────────────
|
|
42
|
+
# 3. Memory Usage
|
|
43
|
+
# ─────────────────────────────────────────────
|
|
44
|
+
def memory_usage(func):
|
|
45
|
+
"""Measures peak memory allocated during function execution."""
|
|
46
|
+
@wraps(func)
|
|
47
|
+
def wrapper(*args, **kwargs):
|
|
48
|
+
tracemalloc.start()
|
|
49
|
+
result = func(*args, **kwargs)
|
|
50
|
+
current, peak = tracemalloc.get_traced_memory()
|
|
51
|
+
tracemalloc.stop()
|
|
52
|
+
|
|
53
|
+
# Auto-format to the most readable unit
|
|
54
|
+
if peak < 1024:
|
|
55
|
+
peak_str = f"{peak} B"
|
|
56
|
+
elif peak < 1024 ** 2:
|
|
57
|
+
peak_str = f"{peak / 1024:.2f} KB"
|
|
58
|
+
else:
|
|
59
|
+
peak_str = f"{peak / (1024 ** 2):.2f} MB"
|
|
60
|
+
|
|
61
|
+
if current < 1024:
|
|
62
|
+
curr_str = f"{current} B"
|
|
63
|
+
elif current < 1024 ** 2:
|
|
64
|
+
curr_str = f"{current / 1024:.2f} KB"
|
|
65
|
+
else:
|
|
66
|
+
curr_str = f"{current / (1024 ** 2):.2f} MB"
|
|
67
|
+
|
|
68
|
+
print(f"[memory_usage] {func.__name__} -> current: {curr_str}, peak: {peak_str}")
|
|
69
|
+
return result
|
|
70
|
+
return wrapper
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
# ─────────────────────────────────────────────
|
|
74
|
+
# 4. Thread Time
|
|
75
|
+
# ─────────────────────────────────────────────
|
|
76
|
+
def thread_time(func):
|
|
77
|
+
"""Measures CPU time consumed by the current thread only."""
|
|
78
|
+
@wraps(func)
|
|
79
|
+
def wrapper(*args, **kwargs):
|
|
80
|
+
start = time.thread_time_ns()
|
|
81
|
+
result = func(*args, **kwargs)
|
|
82
|
+
elapsed_ns = time.thread_time_ns() - start
|
|
83
|
+
elapsed = elapsed_ns / 1_000_000_000
|
|
84
|
+
print(f"[thread_time] {func.__name__} -> {elapsed:.6f}s")
|
|
85
|
+
return result
|
|
86
|
+
return wrapper
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
# ─────────────────────────────────────────────
|
|
90
|
+
# 5. Call Count
|
|
91
|
+
# ─────────────────────────────────────────────
|
|
92
|
+
def call_count(func):
|
|
93
|
+
"""Tracks and displays how many times a function has been called."""
|
|
94
|
+
@wraps(func)
|
|
95
|
+
def wrapper(*args, **kwargs):
|
|
96
|
+
wrapper.calls += 1
|
|
97
|
+
result = func(*args, **kwargs)
|
|
98
|
+
print(f"[call_count] {func.__name__} -> called {wrapper.calls} time(s)")
|
|
99
|
+
return result
|
|
100
|
+
wrapper.calls = 0
|
|
101
|
+
return wrapper
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
# ─────────────────────────────────────────────
|
|
105
|
+
# 6. Function Profile (cProfile)
|
|
106
|
+
# ─────────────────────────────────────────────
|
|
107
|
+
def function_profile(func):
|
|
108
|
+
"""Runs cProfile on the function and prints a profiling summary."""
|
|
109
|
+
@wraps(func)
|
|
110
|
+
def wrapper(*args, **kwargs):
|
|
111
|
+
profiler = cProfile.Profile()
|
|
112
|
+
profiler.enable()
|
|
113
|
+
result = func(*args, **kwargs)
|
|
114
|
+
profiler.disable()
|
|
115
|
+
|
|
116
|
+
stream = io.StringIO()
|
|
117
|
+
stats = pstats.Stats(profiler, stream=stream)
|
|
118
|
+
stats.strip_dirs()
|
|
119
|
+
stats.sort_stats("cumulative")
|
|
120
|
+
stats.print_stats(10) # top 10 entries
|
|
121
|
+
|
|
122
|
+
print(f"[function_profile] {func.__name__}:")
|
|
123
|
+
print(stream.getvalue())
|
|
124
|
+
return result
|
|
125
|
+
return wrapper
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
# ─────────────────────────────────────────────
|
|
129
|
+
# 7. I/O Time (real − cpu = approximate I/O wait)
|
|
130
|
+
# ─────────────────────────────────────────────
|
|
131
|
+
def io_time(func):
|
|
132
|
+
"""Estimates I/O wait time by computing real_time − cpu_time."""
|
|
133
|
+
@wraps(func)
|
|
134
|
+
def wrapper(*args, **kwargs):
|
|
135
|
+
real_start = time.perf_counter()
|
|
136
|
+
cpu_start = time.process_time()
|
|
137
|
+
result = func(*args, **kwargs)
|
|
138
|
+
real_elapsed = time.perf_counter() - real_start
|
|
139
|
+
cpu_elapsed = time.process_time() - cpu_start
|
|
140
|
+
io_elapsed = max(real_elapsed - cpu_elapsed, 0.0)
|
|
141
|
+
print(
|
|
142
|
+
f"[io_time] {func.__name__} -> "
|
|
143
|
+
f"real: {real_elapsed:.6f}s, "
|
|
144
|
+
f"cpu: {cpu_elapsed:.6f}s, "
|
|
145
|
+
f"io_wait: {io_elapsed:.6f}s"
|
|
146
|
+
)
|
|
147
|
+
return result
|
|
148
|
+
return wrapper
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
# ─────────────────────────────────────────────
|
|
152
|
+
# 8. GC Stats
|
|
153
|
+
# ─────────────────────────────────────────────
|
|
154
|
+
def gc_stats(func):
|
|
155
|
+
"""Reports garbage collection activity during function execution."""
|
|
156
|
+
@wraps(func)
|
|
157
|
+
def wrapper(*args, **kwargs):
|
|
158
|
+
gc.collect() # clean slate
|
|
159
|
+
gc.disable() # temporarily disable auto-GC to measure manually
|
|
160
|
+
stats_before = gc.get_stats() # per-generation stats
|
|
161
|
+
|
|
162
|
+
gc.enable()
|
|
163
|
+
result = func(*args, **kwargs)
|
|
164
|
+
collected = gc.collect() # force a collection and count
|
|
165
|
+
|
|
166
|
+
stats_after = gc.get_stats()
|
|
167
|
+
collections = sum(
|
|
168
|
+
stats_after[i]["collections"] - stats_before[i]["collections"]
|
|
169
|
+
for i in range(len(stats_after))
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
print(
|
|
173
|
+
f"[gc_stats] {func.__name__} -> "
|
|
174
|
+
f"collected: {collected} objects, "
|
|
175
|
+
f"gc runs: {collections}"
|
|
176
|
+
)
|
|
177
|
+
return result
|
|
178
|
+
return wrapper
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
# ─────────────────────────────────────────────
|
|
182
|
+
# 9. Object Count
|
|
183
|
+
# ─────────────────────────────────────────────
|
|
184
|
+
def object_count(func):
|
|
185
|
+
"""Tracks the net number of new Python objects created by the function."""
|
|
186
|
+
@wraps(func)
|
|
187
|
+
def wrapper(*args, **kwargs):
|
|
188
|
+
gc.collect()
|
|
189
|
+
before = len(gc.get_objects())
|
|
190
|
+
result = func(*args, **kwargs)
|
|
191
|
+
gc.collect()
|
|
192
|
+
after = len(gc.get_objects())
|
|
193
|
+
net_new = after - before
|
|
194
|
+
print(
|
|
195
|
+
f"[object_count] {func.__name__} -> "
|
|
196
|
+
f"before: {before}, after: {after}, "
|
|
197
|
+
f"net new: {net_new:+d}"
|
|
198
|
+
)
|
|
199
|
+
return result
|
|
200
|
+
return wrapper
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: speedopy
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Zero-dependency performance measurement decorators for Python
|
|
5
|
+
Author-email: AayushtheCoder01 <aayushkumarkumar1234@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/AayushtheCoder01/speedopy
|
|
8
|
+
Project-URL: Repository, https://github.com/AayushtheCoder01/speedopy
|
|
9
|
+
Project-URL: Issues, https://github.com/AayushtheCoder01/speedopy/issues
|
|
10
|
+
Keywords: performance,profiling,benchmark,decorator,timing,memory
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Software Development :: Testing
|
|
22
|
+
Classifier: Topic :: System :: Benchmark
|
|
23
|
+
Requires-Python: >=3.8
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# speedopy
|
|
29
|
+
|
|
30
|
+
Zero-dependency performance measurement decorators for Python.
|
|
31
|
+
|
|
32
|
+
Just import a decorator, slap it on your function, and instantly see metrics printed to the console. No config, no setup, no external packages.
|
|
33
|
+
|
|
34
|
+
## Install
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install speedopy
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Decorators
|
|
41
|
+
|
|
42
|
+
| Decorator | What It Measures |
|
|
43
|
+
|---|---|
|
|
44
|
+
| `cpu_time` | CPU processing time (excludes I/O) |
|
|
45
|
+
| `real_time` | Wall-clock time |
|
|
46
|
+
| `memory_usage` | Peak memory allocated (auto-formats B/KB/MB) |
|
|
47
|
+
| `thread_time` | CPU time for the current thread only |
|
|
48
|
+
| `call_count` | Number of times a function has been called |
|
|
49
|
+
| `function_profile` | Full cProfile report (top 10 calls) |
|
|
50
|
+
| `io_time` | I/O wait time (real - cpu) |
|
|
51
|
+
| `gc_stats` | Garbage collections triggered |
|
|
52
|
+
| `object_count` | Net new Python objects created |
|
|
53
|
+
|
|
54
|
+
## Usage
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
from speedopy import cpu_time, memory_usage, io_time
|
|
58
|
+
|
|
59
|
+
@cpu_time
|
|
60
|
+
@memory_usage
|
|
61
|
+
@io_time
|
|
62
|
+
def process_data():
|
|
63
|
+
data = [i ** 2 for i in range(1_000_000)]
|
|
64
|
+
return sum(data)
|
|
65
|
+
|
|
66
|
+
process_data()
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Output:
|
|
70
|
+
```
|
|
71
|
+
[io_time] process_data -> real: 0.182345s, cpu: 0.171875s, io_wait: 0.010470s
|
|
72
|
+
[memory_usage] process_data -> current: 472 B, peak: 8.06 MB
|
|
73
|
+
[cpu_time] process_data -> 0.183291s
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Stack Multiple Decorators
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from speedopy import cpu_time, memory_usage, call_count
|
|
80
|
+
|
|
81
|
+
@call_count
|
|
82
|
+
@cpu_time
|
|
83
|
+
@memory_usage
|
|
84
|
+
def my_func():
|
|
85
|
+
return sum(range(500_000))
|
|
86
|
+
|
|
87
|
+
my_func() # [call_count] my_func -> called 1 time(s)
|
|
88
|
+
my_func() # [call_count] my_func -> called 2 time(s)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Requirements
|
|
92
|
+
|
|
93
|
+
- Python 3.8+
|
|
94
|
+
- No external dependencies
|
|
95
|
+
|
|
96
|
+
## License
|
|
97
|
+
|
|
98
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
speedopy
|