Skip to content

Commit

Permalink
fix(record): fix performance plotting regression (#774)
Browse files Browse the repository at this point in the history
* Fixes regressions introduced in Database access refactor #676 affecting plotting of performance plots.

* Re-uses record.write_event for video events.

* Modifies plot_performance to use color-blind-friendly markers

* black/flake8
  • Loading branch information
abrichr authored Jun 18, 2024
1 parent 835bb17 commit 44371e9
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 21 deletions.
52 changes: 36 additions & 16 deletions openadapt/plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from collections import defaultdict
from io import BytesIO
from itertools import cycle
import math
import os
import unicodedata
Expand All @@ -15,7 +16,7 @@

from openadapt.config import PERFORMANCE_PLOTS_DIR_PATH, config
from openadapt.models import ActionEvent
from openadapt import common, utils
from openadapt import common, models, utils


# TODO: move parameters to config
Expand Down Expand Up @@ -321,15 +322,15 @@ def display_event(


def plot_performance(
recording_timestamp: float = None,
recording: models.Recording | None = None,
view_file: bool = False,
save_file: bool = True,
dark_mode: bool = False,
) -> str:
"""Plot the performance of the event processing and writing.
Args:
recording_timestamp: The timestamp of the recording (defaults to latest)
recording: The Recording whose performance to plot (defaults to latest).
view_file: Whether to view the file after saving it.
save_file: Whether to save the file.
dark_mode: Whether to use dark mode.
Expand All @@ -339,34 +340,56 @@ def plot_performance(
"""
type_to_proc_times = defaultdict(list)
type_to_timestamps = defaultdict(list)
event_types = set()

if dark_mode:
plt.style.use("dark_background")

# avoid circular import
from openadapt.db import crud

if not recording_timestamp:
recording_timestamp = crud.get_latest_recording().timestamp
perf_stats = crud.get_perf_stats(recording_timestamp)
if not recording:
recording = crud.get_latest_recording()
session = crud.get_new_session(read_only=True)
perf_stats = crud.get_perf_stats(session, recording)
for perf_stat in perf_stats:
event_type = perf_stat.event_type
start_time = perf_stat.start_time
end_time = perf_stat.end_time
type_to_proc_times[event_type].append(end_time - start_time)
event_types.add(event_type)
type_to_timestamps[event_type].append(start_time)

fig, ax = plt.subplots(1, 1, figsize=(20, 10))

# Define markers to distinguish different event types
markers = [
"o",
"s",
"D",
"^",
"v",
">",
"<",
"p",
"*",
"h",
"H",
"+",
"x",
"X",
"d",
"|",
"_",
]
marker_cycle = cycle(markers)

for event_type in type_to_proc_times:
x = type_to_timestamps[event_type]
y = type_to_proc_times[event_type]
ax.scatter(x, y, label=event_type)
ax.scatter(x, y, label=event_type, marker=next(marker_cycle))

ax.legend()
ax.set_ylabel("Duration (seconds)")

mem_stats = crud.get_memory_stats(recording_timestamp)
mem_stats = crud.get_memory_stats(session, recording)
timestamps = []
mem_usages = []
for mem_stat in mem_stats:
Expand All @@ -383,21 +406,18 @@ def plot_performance(
memory_ax.set_ylabel("Memory Usage (bytes)")

if len(mem_usages) > 0:
# Get the handles and labels from both axes
handles1, labels1 = ax.get_legend_handles_labels()
handles2, labels2 = memory_ax.get_legend_handles_labels()

# Combine the handles and labels from both axes
all_handles = handles1 + handles2
all_labels = labels1 + labels2

ax.legend(all_handles, all_labels)

ax.set_title(f"{recording_timestamp=}")
ax.set_title(f"{recording.timestamp=}")

# TODO: add PROC_WRITE_BY_EVENT_TYPE
if save_file:
fname_parts = ["performance", str(recording_timestamp)]
fname_parts = ["performance", str(recording.timestamp)]
fname = "-".join(fname_parts) + ".png"
os.makedirs(PERFORMANCE_PLOTS_DIR_PATH, exist_ok=True)
fpath = os.path.join(PERFORMANCE_PLOTS_DIR_PATH, fname)
Expand Down
27 changes: 22 additions & 5 deletions openadapt/record.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,10 @@

EVENT_TYPES = ("screen", "action", "window")
LOG_LEVEL = "INFO"
# whether to write events of each type in a separate process
PROC_WRITE_BY_EVENT_TYPE = {
"screen": True,
"screen/video": True,
"action": True,
"window": True,
}
Expand Down Expand Up @@ -183,8 +185,15 @@ def process_events(
# XXX TODO: mitigate
if event.type == "screen":
prev_screen_event = event
if config.RECORD_VIDEO and config.RECORD_FULL_VIDEO:
video_write_q.put(event)
if config.RECORD_FULL_VIDEO:
video_event = event._replace(type="screen/video")
process_event(
video_event,
video_write_q,
write_video_event,
recording,
perf_q,
)
num_video_events.value += 1
elif event.type == "window":
prev_window_event = event
Expand Down Expand Up @@ -216,7 +225,14 @@ def process_events(
num_screen_events.value += 1
prev_saved_screen_timestamp = prev_screen_event.timestamp
if config.RECORD_VIDEO and not config.RECORD_FULL_VIDEO:
video_write_q.put(prev_screen_event)
prev_video_event = prev_screen_event._replace(type="screen/video")
process_event(
prev_video_event,
video_write_q,
write_video_event,
recording,
perf_q,
)
num_video_events.value += 1
if prev_saved_window_timestamp < prev_window_event.timestamp:
process_event(
Expand Down Expand Up @@ -462,6 +478,7 @@ def write_video_event(
Returns:
dict containing state.
"""
assert event.type == "screen/video"
screenshot_image = event.data
screenshot_timestamp = event.timestamp
force_key_frame = last_pts == 0
Expand All @@ -479,7 +496,7 @@ def write_video_event(
last_pts,
force_key_frame,
)
perf_q.put((f"{event.type}(video)", event.timestamp, utils.get_timestamp()))
perf_q.put((event.type, event.timestamp, utils.get_timestamp()))
return {
**kwargs,
**{
Expand Down Expand Up @@ -1266,7 +1283,7 @@ def record(
video_writer = multiprocessing.Process(
target=write_events,
args=(
"screen",
"screen/video",
write_video_event,
video_write_q,
num_video_events,
Expand Down

0 comments on commit 44371e9

Please sign in to comment.