You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
172 lines
4.9 KiB
172 lines
4.9 KiB
import time
|
|
import tracemalloc
|
|
from functools import wraps
|
|
from typing import Any, Callable
|
|
|
|
import psutil
|
|
from pydantic import BaseModel
|
|
|
|
from swarms.utils.loguru_logger import initialize_logger
|
|
|
|
logger = initialize_logger(log_folder="calculate_func_metrics")
|
|
|
|
|
|
class FunctionMetrics(BaseModel):
|
|
execution_time: float
|
|
memory_usage: float
|
|
cpu_usage: float
|
|
io_operations: int
|
|
function_calls: int
|
|
|
|
|
|
def profile_func(func):
|
|
"""
|
|
Decorator function that profiles the execution of a given function.
|
|
|
|
Args:
|
|
func: The function to be profiled.
|
|
|
|
Returns:
|
|
A wrapper function that profiles the execution of the given function and returns the result along with the metrics.
|
|
|
|
"""
|
|
|
|
def wrapper(*args, **kwargs):
|
|
# Record the initial time, memory usage, CPU usage, and I/O operations
|
|
start_time = time.time()
|
|
start_mem = psutil.Process().memory_info().rss
|
|
start_cpu = psutil.cpu_percent()
|
|
start_io = (
|
|
psutil.disk_io_counters().read_count
|
|
+ psutil.disk_io_counters().write_count
|
|
)
|
|
|
|
# Call the function
|
|
result = func(*args, **kwargs)
|
|
|
|
# Record the final time, memory usage, CPU usage, and I/O operations
|
|
end_time = time.time()
|
|
end_mem = psutil.Process().memory_info().rss
|
|
end_cpu = psutil.cpu_percent()
|
|
end_io = (
|
|
psutil.disk_io_counters().read_count
|
|
+ psutil.disk_io_counters().write_count
|
|
)
|
|
|
|
# Calculate the execution time, memory usage, CPU usage, and I/O operations
|
|
execution_time = end_time - start_time
|
|
memory_usage = (end_mem - start_mem) / (
|
|
1024**2
|
|
) # Convert bytes to MiB
|
|
cpu_usage = end_cpu - start_cpu
|
|
io_operations = end_io - start_io
|
|
|
|
# Return the metrics as a FunctionMetrics object
|
|
metrics = FunctionMetrics(
|
|
execution_time=execution_time,
|
|
memory_usage=memory_usage,
|
|
cpu_usage=cpu_usage,
|
|
io_operations=io_operations,
|
|
function_calls=1, # Each call to the function counts as one function call
|
|
)
|
|
|
|
json_data = metrics.model_dump_json(indent=4)
|
|
|
|
logger.info(f"Function metrics: {json_data}")
|
|
|
|
return result, metrics
|
|
|
|
return wrapper
|
|
|
|
|
|
def profile_all(func: Callable) -> Callable:
|
|
"""
|
|
A decorator to profile memory usage, CPU usage, and I/O operations
|
|
of a function and log the data using loguru.
|
|
|
|
It combines tracemalloc for memory profiling, psutil for CPU and I/O operations,
|
|
and measures execution time.
|
|
|
|
Args:
|
|
func (Callable): The function to be profiled.
|
|
|
|
Returns:
|
|
Callable: The wrapped function with profiling enabled.
|
|
"""
|
|
|
|
@wraps(func)
|
|
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
# Start memory tracking
|
|
tracemalloc.start()
|
|
|
|
# Get initial CPU stats
|
|
process = psutil.Process()
|
|
initial_cpu_times = process.cpu_times()
|
|
|
|
# Get initial I/O stats if available
|
|
try:
|
|
initial_io_counters = process.io_counters()
|
|
io_tracking_available = True
|
|
except AttributeError:
|
|
logger.warning(
|
|
"I/O counters not available on this platform."
|
|
)
|
|
io_tracking_available = False
|
|
|
|
# Start timing the function execution
|
|
start_time = time.time()
|
|
|
|
# Execute the function
|
|
result = func(*args, **kwargs)
|
|
|
|
# Stop timing
|
|
end_time = time.time()
|
|
execution_time = end_time - start_time
|
|
|
|
# Get final CPU stats
|
|
final_cpu_times = process.cpu_times()
|
|
|
|
# Get final I/O stats if available
|
|
if io_tracking_available:
|
|
final_io_counters = process.io_counters()
|
|
io_read_count = (
|
|
final_io_counters.read_count
|
|
- initial_io_counters.read_count
|
|
)
|
|
io_write_count = (
|
|
final_io_counters.write_count
|
|
- initial_io_counters.write_count
|
|
)
|
|
else:
|
|
io_read_count = io_write_count = 0
|
|
|
|
# Get memory usage statistics
|
|
snapshot = tracemalloc.take_snapshot()
|
|
top_stats = snapshot.statistics("lineno")
|
|
|
|
# Calculate CPU usage
|
|
cpu_usage = (
|
|
final_cpu_times.user
|
|
- initial_cpu_times.user
|
|
+ final_cpu_times.system
|
|
- initial_cpu_times.system
|
|
)
|
|
|
|
# Log the data
|
|
logger.info(f"Execution time: {execution_time:.4f} seconds")
|
|
logger.info(f"CPU usage: {cpu_usage:.2f} seconds")
|
|
if io_tracking_available:
|
|
logger.info(
|
|
f"I/O Operations - Read: {io_read_count}, Write: {io_write_count}"
|
|
)
|
|
logger.info("Top memory usage:")
|
|
for stat in top_stats[:10]:
|
|
logger.info(stat)
|
|
|
|
# Stop memory tracking
|
|
tracemalloc.stop()
|
|
|
|
return result
|
|
|
|
return wrapper
|