mirror of
https://github.com/sunnypilot/sunnypilot.git
synced 2026-06-14 03:54:22 +08:00
Compare commits
21 Commits
paramwatch
...
tools
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3375277b3c | ||
|
|
6e06bcb14f | ||
|
|
de30e9a3cf | ||
|
|
c599542dfa | ||
|
|
b737989e64 | ||
|
|
5fbc358fd5 | ||
|
|
fdde1aa6a1 | ||
|
|
961b2a2d30 | ||
|
|
f3d39d481a | ||
|
|
6e037d80ff | ||
|
|
b3ff268f89 | ||
|
|
5901c9b41f | ||
|
|
18f8956e0e | ||
|
|
0aa6f22c26 | ||
|
|
c90f262ce7 | ||
|
|
4a189f828a | ||
|
|
072e18faef | ||
|
|
34e02b6ae5 | ||
|
|
c98cc5d40a | ||
|
|
4a0d8063e5 | ||
|
|
483894cfc8 |
@@ -1,4 +1,3 @@
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.selfdrive.ui.widgets.ssh_key import ssh_key_item
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state
|
||||
from openpilot.system.ui.widgets import Widget
|
||||
@@ -35,7 +34,7 @@ DESCRIPTIONS = {
|
||||
class DeveloperLayout(Widget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._params = Params()
|
||||
self._params = ui_state.params
|
||||
self._is_release = self._params.get_bool("IsReleaseBranch")
|
||||
|
||||
# Build items and keep references for callbacks/state updates
|
||||
|
||||
@@ -3,7 +3,6 @@ import math
|
||||
|
||||
from cereal import messaging, log
|
||||
from openpilot.common.basedir import BASEDIR
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
from openpilot.selfdrive.ui.onroad.driver_camera_dialog import DriverCameraDialog
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state
|
||||
@@ -35,7 +34,7 @@ class DeviceLayout(Widget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self._params = Params()
|
||||
self._params = ui_state.params
|
||||
self._select_language_dialog: MultiOptionDialog | None = None
|
||||
self._driver_camera: DriverCameraDialog | None = None
|
||||
self._pair_device_dialog: PairingDialog | None = None
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from cereal import log
|
||||
from openpilot.common.params import Params, UnknownKeyName
|
||||
from openpilot.common.params import UnknownKeyName
|
||||
from openpilot.system.ui.widgets import Widget
|
||||
from openpilot.system.ui.widgets.list_view import multiple_button_item, toggle_item
|
||||
from openpilot.system.ui.widgets.scroller_tici import Scroller
|
||||
@@ -41,7 +41,7 @@ DESCRIPTIONS = {
|
||||
class TogglesLayout(Widget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._params = Params()
|
||||
self._params = ui_state.params
|
||||
self._is_release = self._params.get_bool("IsReleaseBranch")
|
||||
|
||||
# param, title, desc, icon, needs_restart
|
||||
@@ -198,11 +198,6 @@ class TogglesLayout(Widget):
|
||||
|
||||
self._update_experimental_mode_icon()
|
||||
|
||||
# TODO: make a param control list item so we don't need to manage internal state as much here
|
||||
# refresh toggles from params to mirror external changes
|
||||
for param in self._toggle_defs:
|
||||
self._toggles[param].action_item.set_state(self._params.get_bool(param))
|
||||
|
||||
# these toggles need restart, block while engaged
|
||||
for toggle_def in self._toggle_defs:
|
||||
if self._toggle_defs[toggle_def][3] and toggle_def not in self._locked_toggles:
|
||||
|
||||
@@ -230,9 +230,7 @@ class ModelsLayout(Widget):
|
||||
turn_desire: bool = ui_state.params.get_bool("LaneTurnDesire")
|
||||
live_delay: bool = ui_state.params.get_bool("LagdToggle")
|
||||
|
||||
self.lane_turn_desire_toggle.action_item.set_state(turn_desire)
|
||||
self.lane_turn_value_control.set_visible(turn_desire and advanced_controls)
|
||||
self.lagd_toggle.action_item.set_state(live_delay)
|
||||
self.delay_control.set_visible(not live_delay and advanced_controls)
|
||||
new_step = int(round(100 / CV.MPH_TO_KPH)) if ui_state.is_metric else 100
|
||||
if self.lane_turn_value_control.action_item.value_change_step != new_step:
|
||||
|
||||
@@ -31,6 +31,7 @@ from openpilot.selfdrive.ui.sunnypilot.layouts.settings.steering import Steering
|
||||
from openpilot.selfdrive.ui.sunnypilot.layouts.settings.cruise import CruiseLayout
|
||||
from openpilot.selfdrive.ui.sunnypilot.layouts.settings.visuals import VisualsLayout
|
||||
from openpilot.selfdrive.ui.sunnypilot.layouts.settings.display import DisplayLayout
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state
|
||||
|
||||
# from openpilot.selfdrive.ui.sunnypilot.layouts.settings.navigation import NavigationLayout
|
||||
|
||||
@@ -196,6 +197,10 @@ class SettingsLayoutSP(OP.SettingsLayout):
|
||||
|
||||
return False
|
||||
|
||||
def set_current_panel(self, panel_type: OP.PanelType):
|
||||
super().set_current_panel(panel_type)
|
||||
ui_state.set_active_layout(self._panels[self._current_panel].instance)
|
||||
|
||||
def show_event(self):
|
||||
super().show_event()
|
||||
self._panels[self._current_panel].instance.show_event()
|
||||
|
||||
@@ -320,7 +320,6 @@ class SunnylinkLayout(Widget):
|
||||
self._sunnylink_enabled = ui_state.params.get_bool("SunnylinkEnabled")
|
||||
self._sunnylink_toggle.set_right_value(tr("Dongle ID") + ": " + self._get_sunnylink_dongle_id())
|
||||
self._sunnylink_toggle.action_item.set_enabled(not ui_state.is_onroad())
|
||||
self._sunnylink_toggle.action_item.set_state(self._sunnylink_enabled)
|
||||
self._sunnylink_uploader_toggle.action_item.set_enabled(self._sunnylink_enabled)
|
||||
self.handle_backup_restore_progress()
|
||||
|
||||
|
||||
@@ -55,5 +55,4 @@ class HyundaiSettings(BrandSettings):
|
||||
self.longitudinal_tuning_item.action_item.set_enabled(not longitudinal_tuning_disabled)
|
||||
self.longitudinal_tuning_item.set_description(long_tuning_desc)
|
||||
self.longitudinal_tuning_item.show_description(True)
|
||||
self.longitudinal_tuning_item.action_item.set_selected_button(tuning_param)
|
||||
self.longitudinal_tuning_item.set_visible(self.alpha_long_available)
|
||||
|
||||
41
selfdrive/ui/sunnypilot/ui_helpers.py
Normal file
41
selfdrive/ui/sunnypilot/ui_helpers.py
Normal file
@@ -0,0 +1,41 @@
|
||||
"""
|
||||
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
|
||||
|
||||
This file is part of sunnypilot and is licensed under the MIT License.
|
||||
See the LICENSE.md file in the root directory for more details.
|
||||
"""
|
||||
|
||||
|
||||
def update_item_from_param(item, key, params):
|
||||
if not (action := getattr(item, 'action_item', None)):
|
||||
return
|
||||
|
||||
if hasattr(action, 'set_state'):
|
||||
action.set_state(params.get_bool(key))
|
||||
elif hasattr(action, 'set_value'):
|
||||
action.set_value(params.get(key, return_default=True))
|
||||
else:
|
||||
try:
|
||||
val = int(params.get(key, return_default=True))
|
||||
if hasattr(action, 'selected_button'):
|
||||
action.selected_button = val
|
||||
if hasattr(action, 'current_value'):
|
||||
action.current_value = val
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
|
||||
|
||||
def sync_layout_params(layout, param_name, params):
|
||||
targets = []
|
||||
if toggles := getattr(layout, '_toggles', None):
|
||||
targets.extend([(item, k) for k, item in toggles.items()])
|
||||
|
||||
items = getattr(layout, 'items', []) or getattr(getattr(layout, '_scroller', None), '_items', [])
|
||||
for item in items:
|
||||
action = getattr(item, 'action_item', None)
|
||||
if key := getattr(action, 'param_key', None) or getattr(getattr(action, 'toggle', None), 'param_key', None):
|
||||
targets.append((item, key))
|
||||
|
||||
for item, key in targets:
|
||||
if param_name is None or key == param_name:
|
||||
update_item_from_param(item, key, params)
|
||||
@@ -5,23 +5,49 @@ This file is part of sunnypilot and is licensed under the MIT License.
|
||||
See the LICENSE.md file in the root directory for more details.
|
||||
"""
|
||||
from cereal import messaging, custom
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
|
||||
from openpilot.sunnypilot.common.params import Params
|
||||
from openpilot.sunnypilot.sunnylink.sunnylink_state import SunnylinkState
|
||||
from openpilot.selfdrive.ui.sunnypilot.ui_helpers import sync_layout_params
|
||||
|
||||
|
||||
class UIStateSP:
|
||||
def __init__(self):
|
||||
self.params = Params()
|
||||
self.params.add_watcher(self.on_param_change)
|
||||
self.params.start()
|
||||
self.sm_services_ext = [
|
||||
"modelManagerSP", "selfdriveStateSP", "longitudinalPlanSP", "backupManagerSP",
|
||||
"gpsLocation", "liveTorqueParameters", "carStateSP", "liveMapDataSP", "carParamsSP", "liveDelay"
|
||||
]
|
||||
|
||||
self.sunnylink_state = SunnylinkState()
|
||||
self.active_layout = None
|
||||
self.changed_params = set()
|
||||
|
||||
def set_active_layout(self, layout):
|
||||
self.active_layout = layout
|
||||
if layout:
|
||||
sync_layout_params(layout, None, self.params)
|
||||
|
||||
def on_param_change(self, param_name):
|
||||
self.changed_params.add(param_name)
|
||||
|
||||
def update(self) -> None:
|
||||
self.sunnylink_state.start()
|
||||
|
||||
if not self.params.is_watching():
|
||||
cloudlog.warning("ParamWatcher thread died, restarting...")
|
||||
self.params.start()
|
||||
|
||||
if self.changed_params:
|
||||
while self.changed_params:
|
||||
self.changed_params.pop()
|
||||
|
||||
if self.active_layout:
|
||||
sync_layout_params(self.active_layout, None, self.params)
|
||||
|
||||
def update_params(self) -> None:
|
||||
CP_SP_bytes = self.params.get("CarParamsSPPersistent")
|
||||
if CP_SP_bytes is not None:
|
||||
|
||||
@@ -6,7 +6,6 @@ from collections.abc import Callable
|
||||
from enum import Enum
|
||||
from cereal import messaging, car, log
|
||||
from openpilot.common.filter_simple import FirstOrderFilter
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
from openpilot.selfdrive.ui.lib.prime_state import PrimeState
|
||||
from openpilot.system.ui.lib.application import gui_app
|
||||
@@ -34,7 +33,6 @@ class UIState(UIStateSP):
|
||||
|
||||
def _initialize(self):
|
||||
UIStateSP.__init__(self)
|
||||
self.params = Params()
|
||||
self.sm = messaging.SubMaster(
|
||||
[
|
||||
"modelV2",
|
||||
|
||||
0
sunnypilot/tools/__init__.py
Normal file
0
sunnypilot/tools/__init__.py
Normal file
171
sunnypilot/tools/profile_params.py
Executable file
171
sunnypilot/tools/profile_params.py
Executable file
@@ -0,0 +1,171 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import ctypes
|
||||
import csv
|
||||
import os
|
||||
import platform
|
||||
import random
|
||||
import select
|
||||
import struct
|
||||
import sys
|
||||
import time
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.ticker as ticker
|
||||
from collections import defaultdict
|
||||
|
||||
from openpilot.system.hardware.hw import Paths
|
||||
|
||||
from openpilot.sunnypilot.common.param_watcher import ParamWatcher, IN_CLOSE_WRITE, IN_MOVED_TO
|
||||
|
||||
IN_ACCESS = 0x00000001
|
||||
|
||||
|
||||
def get_linux_monitor(params_path, reads, writes):
|
||||
libc = ctypes.CDLL('libc.so.6')
|
||||
fd = libc.inotify_init()
|
||||
if fd < 0:
|
||||
return None
|
||||
|
||||
mask = IN_ACCESS | IN_MOVED_TO | IN_CLOSE_WRITE
|
||||
if libc.inotify_add_watch(fd, params_path.encode(), mask) < 0:
|
||||
return None
|
||||
|
||||
poll_obj = select.epoll()
|
||||
poll_obj.register(fd, select.EPOLLIN)
|
||||
|
||||
def monitor():
|
||||
for fileno, _ in poll_obj.poll(0.1):
|
||||
if fileno == fd:
|
||||
buffer = os.read(fd, 2048)
|
||||
i = 0
|
||||
while i + 16 <= len(buffer):
|
||||
wd, mask, cookie, name_len = struct.unpack_from("iIII", buffer, i)
|
||||
name = buffer[i+16:i+16+name_len].rstrip(b"\0").decode('utf-8', 'ignore')
|
||||
if name and not name.startswith("."):
|
||||
if mask & IN_ACCESS:
|
||||
reads[name] += 1
|
||||
elif mask & (IN_MOVED_TO | IN_CLOSE_WRITE):
|
||||
writes[name] += 1
|
||||
i += 16 + name_len
|
||||
|
||||
def cleanup():
|
||||
os.close(fd)
|
||||
return monitor, cleanup
|
||||
|
||||
def get_darwin_monitor(params_path, reads, writes):
|
||||
print("WARNING: macOS only reports WRITES.")
|
||||
|
||||
def callback(name):
|
||||
writes[name] += 1
|
||||
|
||||
watcher = ParamWatcher()
|
||||
watcher.add_watcher(callback)
|
||||
|
||||
def monitor():
|
||||
time.sleep(0.1)
|
||||
|
||||
def cleanup():
|
||||
if callback in watcher._callbacks:
|
||||
watcher._callbacks.remove(callback)
|
||||
return monitor, cleanup
|
||||
|
||||
def profile_params():
|
||||
parser = argparse.ArgumentParser(description="Profile Params I/O")
|
||||
parser.add_argument("--timeout", type=int, default=30, help="Timeout in minutes (default: 30 mins)")
|
||||
default_out = os.path.join(os.path.dirname(os.path.abspath(__file__)), f"params_profile_{random.randrange(99999)}.csv")
|
||||
parser.add_argument("--out", type=str, default=default_out, help="Output CSV file")
|
||||
args = parser.parse_args()
|
||||
|
||||
path = Paths.params_root()
|
||||
if not os.path.exists(path):
|
||||
return print(f"Error: {path} not found")
|
||||
|
||||
print(f"Profiling Params I/O at: {path}\nPress CTRL+C to stop.")
|
||||
reads, writes = defaultdict(int), defaultdict(int)
|
||||
|
||||
setup = get_linux_monitor if platform.system() == "Linux" else \
|
||||
get_darwin_monitor if platform.system() == "Darwin" else None
|
||||
|
||||
if not setup:
|
||||
return print("Unsupported platform")
|
||||
monitor, cleanup = setup(path, reads, writes) or (None, None)
|
||||
|
||||
if not monitor:
|
||||
return print("Failed to initialize monitor")
|
||||
|
||||
start_time = time.monotonic()
|
||||
timeout_seconds = args.timeout * 60
|
||||
last_print = start_time
|
||||
|
||||
try:
|
||||
while True:
|
||||
monitor()
|
||||
if time.monotonic() - last_print > 1.0:
|
||||
sys.stdout.write(".")
|
||||
sys.stdout.flush()
|
||||
last_print = time.monotonic()
|
||||
|
||||
if args.timeout > 0 and (time.monotonic() - start_time) > timeout_seconds:
|
||||
print("\nTimeout reached.")
|
||||
break
|
||||
except KeyboardInterrupt:
|
||||
print("\n\nStopping...")
|
||||
finally:
|
||||
cleanup()
|
||||
|
||||
duration = time.monotonic() - start_time
|
||||
|
||||
|
||||
with open(args.out, 'w', newline='') as csvfile:
|
||||
writer = csv.writer(csvfile)
|
||||
writer.writerow(['Param Name', 'Reads/sec', 'Writes/sec', 'Total Reads', 'Total Writes'])
|
||||
for k in sorted(set(reads) | set(writes), key=lambda k: reads[k] + writes[k], reverse=True):
|
||||
writer.writerow([k, f"{reads[k]/duration:.1f}", f"{writes[k]/duration:.1f}", reads[k], writes[k]])
|
||||
print(f"CSV report saved to {args.out}")
|
||||
|
||||
|
||||
data = []
|
||||
for k in sorted(set(reads) | set(writes), key=lambda k: reads[k] + writes[k], reverse=True):
|
||||
data.append((k, reads[k]/duration, writes[k]/duration))
|
||||
|
||||
if data:
|
||||
data = data[:10]
|
||||
names = [x[0] for x in data]
|
||||
read_rates = [x[1] for x in data]
|
||||
write_rates = [x[2] for x in data]
|
||||
|
||||
bar_height = 0.35
|
||||
plt.figure(figsize=(12, len(names) * 0.5 + 2), dpi=150)
|
||||
y_pos = range(len(names))
|
||||
|
||||
y_pos_reads = [y - bar_height/2 for y in y_pos]
|
||||
y_pos_writes = [y + bar_height/2 for y in y_pos]
|
||||
|
||||
plt.barh(y_pos_reads, read_rates, height=bar_height, align='center', color='dodgerblue', alpha=0.8, label='Reads/sec')
|
||||
plt.barh(y_pos_writes, write_rates, height=bar_height, align='center', color='red', alpha=0.8, label='Writes/sec')
|
||||
|
||||
for i, (r_rate, w_rate) in enumerate(zip(read_rates, write_rates, strict=False)):
|
||||
if r_rate > 0:
|
||||
plt.text(r_rate, y_pos_reads[i], f"{r_rate:.2f}", va='center', fontsize=8, color='#005a9e', fontweight='bold')
|
||||
if w_rate > 0:
|
||||
plt.text(w_rate, y_pos_writes[i], f"{w_rate:.2f}", va='center', fontsize=8, color='#a30000', fontweight='bold')
|
||||
|
||||
max_val = max(max(read_rates), max(write_rates)) if read_rates else 0
|
||||
|
||||
plt.xlim(0, max_val * 1.15)
|
||||
plt.yticks(y_pos, names)
|
||||
plt.xlabel('Rate (Hz)')
|
||||
plt.title('Top 10 Params I/O Profile')
|
||||
plt.legend()
|
||||
plt.grid(axis='x', linestyle='--', alpha=0.5)
|
||||
plt.gca().xaxis.set_major_locator(ticker.MaxNLocator(integer=True, nbins='auto'))
|
||||
plt.tight_layout()
|
||||
plt.gca().invert_yaxis()
|
||||
|
||||
plot_filename = os.path.splitext(args.out)[0] + ".png"
|
||||
plt.savefig(plot_filename)
|
||||
print(f"Plot saved to {plot_filename}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
profile_params()
|
||||
@@ -16,6 +16,7 @@ from openpilot.system.ui.widgets.list_view import ListItem, ToggleAction, ItemAc
|
||||
_resolve_value, BUTTON_WIDTH, BUTTON_HEIGHT, TEXT_PADDING
|
||||
from openpilot.system.ui.sunnypilot.lib.styles import style
|
||||
from openpilot.system.ui.sunnypilot.widgets.option_control import OptionControlSP, LABEL_WIDTH
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state
|
||||
|
||||
|
||||
class ToggleActionSP(ToggleAction):
|
||||
@@ -247,6 +248,9 @@ class ListItemSP(ListItem):
|
||||
|
||||
def toggle_item_sp(title: str | Callable[[], str], description: str | Callable[[], str] | None = None, initial_state: bool = False,
|
||||
callback: Callable | None = None, icon: str = "", enabled: bool | Callable[[], bool] = True, param: str | None = None) -> ListItemSP:
|
||||
if param is None and hasattr(ui_state.params, 'last_accessed_param') and ui_state.params.last_accessed_param:
|
||||
param = ui_state.params.last_accessed_param
|
||||
ui_state.params.last_accessed_param = None
|
||||
action = ToggleActionSP(initial_state=initial_state, enabled=enabled, callback=callback, param=param)
|
||||
return ListItemSP(title=title, description=description, action_item=action, icon=icon, callback=callback)
|
||||
|
||||
@@ -254,6 +258,9 @@ def toggle_item_sp(title: str | Callable[[], str], description: str | Callable[[
|
||||
def multiple_button_item_sp(title: str | Callable[[], str], description: str | Callable[[], str], buttons: list[str | Callable[[], str]],
|
||||
selected_index: int = 0, button_width: int = style.BUTTON_WIDTH, callback: Callable = None,
|
||||
icon: str = "", param: str | None = None, inline: bool = False) -> ListItemSP:
|
||||
if param is None and hasattr(ui_state.params, 'last_accessed_param') and ui_state.params.last_accessed_param:
|
||||
param = ui_state.params.last_accessed_param
|
||||
ui_state.params.last_accessed_param = None
|
||||
action = MultipleButtonActionSP(buttons, button_width, selected_index, callback=callback, param=param)
|
||||
return ListItemSP(title=title, description=description, icon=icon, action_item=action, inline=inline)
|
||||
|
||||
|
||||
109
tools/profile_params.py
Executable file
109
tools/profile_params.py
Executable file
@@ -0,0 +1,109 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import sys
|
||||
import platform
|
||||
import struct
|
||||
import select
|
||||
import time
|
||||
import ctypes
|
||||
from collections import defaultdict
|
||||
|
||||
from openpilot.system.hardware.hw import Paths
|
||||
|
||||
from sunnypilot.common.param_watcher import ParamWatcher, IN_CLOSE_WRITE, IN_MOVED_TO
|
||||
|
||||
IN_ACCESS = 0x00000001
|
||||
|
||||
|
||||
def get_linux_monitor(params_path, reads, writes):
|
||||
libc = ctypes.CDLL('libc.so.6')
|
||||
fd = libc.inotify_init()
|
||||
if fd < 0:
|
||||
return None
|
||||
|
||||
mask = IN_ACCESS | IN_MOVED_TO | IN_CLOSE_WRITE
|
||||
if libc.inotify_add_watch(fd, params_path.encode(), mask) < 0:
|
||||
return None
|
||||
|
||||
poll_obj = select.epoll()
|
||||
poll_obj.register(fd, select.EPOLLIN)
|
||||
|
||||
def monitor():
|
||||
for fileno, _ in poll_obj.poll(0.1):
|
||||
if fileno == fd:
|
||||
buffer = os.read(fd, 4096)
|
||||
i = 0
|
||||
while i + 16 <= len(buffer):
|
||||
wd, mask, cookie, name_len = struct.unpack_from("iIII", buffer, i)
|
||||
name = buffer[i+16:i+16+name_len].rstrip(b"\0").decode('utf-8', 'ignore')
|
||||
if name and not name.startswith("."):
|
||||
if mask & IN_ACCESS:
|
||||
reads[name] += 1
|
||||
elif mask & (IN_MOVED_TO | IN_CLOSE_WRITE):
|
||||
writes[name] += 1
|
||||
i += 16 + name_len
|
||||
|
||||
def cleanup():
|
||||
os.close(fd)
|
||||
return monitor, cleanup
|
||||
|
||||
def get_darwin_monitor(params_path, reads, writes):
|
||||
print("WARNING: macOS only reports WRITES.")
|
||||
|
||||
def callback(name):
|
||||
writes[name] += 1
|
||||
|
||||
watcher = ParamWatcher()
|
||||
watcher.add_watcher(callback)
|
||||
|
||||
def monitor():
|
||||
time.sleep(0.1)
|
||||
|
||||
def cleanup():
|
||||
if callback in watcher._callbacks:
|
||||
watcher._callbacks.remove(callback)
|
||||
return monitor, cleanup
|
||||
|
||||
def profile_params():
|
||||
path = Paths.params_root()
|
||||
if not os.path.exists(path):
|
||||
return print(f"Error: {path} not found")
|
||||
|
||||
print(f"Profiling Params I/O at: {path}\nPress CTRL+C to stop.")
|
||||
reads, writes = defaultdict(int), defaultdict(int)
|
||||
|
||||
setup = get_linux_monitor if platform.system() == "Linux" else \
|
||||
get_darwin_monitor if platform.system() == "Darwin" else None
|
||||
|
||||
if not setup:
|
||||
return print("Unsupported platform")
|
||||
monitor, cleanup = setup(path, reads, writes) or (None, None)
|
||||
|
||||
if not monitor:
|
||||
return print("Failed to initialize monitor")
|
||||
|
||||
start_time = time.monotonic()
|
||||
last_print = start_time
|
||||
|
||||
try:
|
||||
while True:
|
||||
monitor()
|
||||
if time.monotonic() - last_print > 1.0:
|
||||
sys.stdout.write(".")
|
||||
sys.stdout.flush()
|
||||
last_print = time.monotonic()
|
||||
except KeyboardInterrupt:
|
||||
print("\n\nStopping...")
|
||||
finally:
|
||||
cleanup()
|
||||
|
||||
duration = time.monotonic() - start_time
|
||||
print(f"\n\n=== Params I/O Profile Report ({duration:.1f}s) ===")
|
||||
print(f"{'Param Name':<40} | {'Reads/sec':<10} | {'Writes/sec':<10} | {'Total Reads':<12} | {'Total Writes':<12}")
|
||||
print("-" * 95)
|
||||
|
||||
for k in sorted(set(reads) | set(writes), key=lambda k: reads[k] + writes[k], reverse=True):
|
||||
print(f"{k:<40} | {reads[k]/duration:<10.1f} | {writes[k]/duration:<10.1f} | {reads[k]:<12} | {writes[k]:<12}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
profile_params()
|
||||
Reference in New Issue
Block a user