Skip to content

Commit 69c50ef

Browse files
committed
0.1.1
1 parent 52a603d commit 69c50ef

File tree

4 files changed

+50
-7
lines changed

4 files changed

+50
-7
lines changed

README.md

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
## Overview
88

9-
`metrit` is a Python package designed to simplify the process of measuring the execution resources of your functions through a straightforward decorator.
9+
`metrit` is a Python package designed to simplify the process of measuring the execution resources of your functions through a straightforward decorator. `metrit` decorator is a powerful tool for detailed performance analysis of Python functions, offering insights into resource utilization and potential bottlenecks. Its robust handling of process isolation and monitoring ensures accurate measurements, even for complex functions that involve multiple processes.
1010

1111
## Installation
1212

@@ -214,6 +214,42 @@ If an error occurs while executing the decorated function in non-isolated mode o
214214

215215
- Deprecation warnings will be added before removing a feature.
216216

217+
## How metrit works
218+
219+
Here's a detailed explanation of how `metrit` operates:
220+
221+
1. Function Process Execution:
222+
223+
- The decorated function is initially executed in a separate process if `isolate = True`, referred to as the function process.
224+
- If this process crashes, the function will be executed in the main process.
225+
226+
2. Monitor Process Setup:
227+
228+
- Before starting the function process, another process, called the monitor process, is created to monitor the resources of the function process.
229+
- The monitor process takes an initial snapshot of the resources in the function process to use as a baseline.
230+
231+
3. Resource Monitoring:
232+
233+
- The monitor process continuously checks the resources of the function process, starting with an interval of 0.1 seconds, which can scale up to 5 seconds for longer-running functions.
234+
- This adaptive refresh rate minimizes the monitoring overhead for time-consuming functions.
235+
236+
4. Completion and Data Collection:
237+
238+
- Upon the function process completion, a signal is sent to the monitor process to stop monitoring.
239+
- The data collected by the monitor process is adjusted by subtracting the initial snapshot to ensure precision.
240+
- The final values are then printed.
241+
242+
5. Handling Failures and Parameters:
243+
244+
- If the function process fails, or if the `isolate` parameter is set to `False`, the function will execute in the main process while the monitor process continues its monitoring.
245+
- The recursion checker for isolated functions uses queues to communicate the function call stack.
246+
- Methods are not isolated and will always run in the main process, as if `isolate` is `False`.
247+
248+
6. Child Process Monitoring:
249+
250+
- When `find_children` is set to `True`, the library measures all processes spawned by the function process.
251+
- This feature is useful for nested functions or functions that spawn additional processes.
252+
217253
## Contributing
218254

219255
Contributions are welcome! Please follow these guidelines when contributing:

examples/examples.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def cpu_intensive(a: int = 1, b: int = 2) -> int:
6060
return a + b
6161

6262

63-
@metrit(isolate=True)
63+
@metrit
6464
def recursive_func(n):
6565
if n < 2:
6666
return n
@@ -92,7 +92,7 @@ def simulate_writes_and_reads(num_writes=5_000, data_size=1024):
9292
return num_writes + data_size
9393

9494

95-
# @metrit(verbose=True, find_children=True, isolate=True)
95+
@metrit(verbose=True, find_children=True, isolate=True)
9696
def main():
9797

9898
print("---CLASS EXAMPLES---")

metrit/Monitoring.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,9 @@ def pool_stats(self):
157157
stats = self.stat_collector.collect_cpu_ram_stats(
158158
stats, self.find_children, refresh_rate, self.success_queue
159159
)
160+
161+
# This call will help if find children = True and some of them die before the STOP signal is received so IO counters for dead processes will not be collected.
162+
stats = self.stat_collector.collect_io_counters(stats, self.find_children, self.success_queue)
160163
if not self.stop_queue.empty():
161164
message = self.stop_queue.get()
162165
if message == "STOP":

metrit/StatCollector.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,6 @@ def collect_cpu_ram_stats(
6363
stats[child.pid].rss_bytes.append(rss_bytes)
6464
stats[child.pid].vms_bytes.append(vms_bytes)
6565
except Exception:
66-
if child.pid in stats:
67-
del stats[child.pid]
6866
continue # It is possible for some children processes to not exist, so we skip them
6967

7068
sucesss_queue.put(True)
@@ -150,8 +148,14 @@ def collect_io_counters(self, stats, find_children: bool, sucesss_queue: Queue)
150148
stats[child.pid].io_read_bytes = io_read_bytes
151149
stats[child.pid].io_write_bytes = io_write_bytes
152150
except Exception:
153-
if child.pid in stats:
154-
del stats[child.pid]
151+
if child.pid in stats and (
152+
stats[child.pid].io_read_count != 0
153+
and stats[child.pid].io_write_count != 0 # noqa: W503
154+
and stats[child.pid].io_read_bytes != 0 # noqa: W503
155+
and stats[child.pid].io_write_bytes != 0 # noqa: W503
156+
):
157+
continue # This continue is to make the code more clear, but the whole condition could be removed
158+
155159
continue # It is possible for some children processes to not exist, so we skip them
156160

157161
sucesss_queue.put(True)

0 commit comments

Comments
 (0)