Skip to content

Commit

Permalink
Improve memory usage reporting in GUI
Browse files Browse the repository at this point in the history
This commit improves memory usage reporting in GUI by:
* Having RealizationDelegate be hoverable, and displaying usage in tooltip
* Displaying current and maximum memory usage in status bar
  • Loading branch information
jonathan-eq committed May 14, 2024
1 parent 2e48eee commit 004ea8a
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 15 deletions.
22 changes: 20 additions & 2 deletions src/ert/gui/model/snapshot.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
FileRole = Qt.UserRole + 6
RealIens = Qt.UserRole + 7
IterNum = Qt.UserRole + 12
MemoryUsageRole = Qt.UserRole + 13

# Indicates what type the underlying data is
IsEnsembleRole = Qt.UserRole + 8
Expand Down Expand Up @@ -223,9 +224,21 @@ def _add_partial_snapshot(self, partial: PartialSnapshot, iter_: int):
if job.index:
job_node.data[ids.INDEX] = job.index
if job.current_memory_usage:
job_node.data[ids.CURRENT_MEMORY_USAGE] = job.current_memory_usage
current_memory_usage = float(job.current_memory_usage)
job_node.data[ids.CURRENT_MEMORY_USAGE] = current_memory_usage
real_node.data[ids.CURRENT_MEMORY_USAGE] = current_memory_usage
self.root.data[ids.CURRENT_MEMORY_USAGE] = current_memory_usage
if job.max_memory_usage:
job_node.data[ids.MAX_MEMORY_USAGE] = job.max_memory_usage
max_memory_usage = float(job.max_memory_usage)
job_node.data[ids.MAX_MEMORY_USAGE] = max_memory_usage
real_node.data[ids.MAX_MEMORY_USAGE] = max(
real_node.data.get(ids.MAX_MEMORY_USAGE, -1),
max_memory_usage,
)
self.root.data[ids.MAX_MEMORY_USAGE] = max(
self.root.data.get(ids.MAX_MEMORY_USAGE, -1),
max_memory_usage,
)

# Errors may be unset as the queue restarts the job
job_node.data[ids.ERROR] = job.error if job.error else ""
Expand Down Expand Up @@ -406,6 +419,11 @@ def _real_data(_index: QModelIndex, node: Node, role: int):
return node.data[REAL_STATUS_COLOR]
if role == StatusRole:
return node.data[ids.STATUS]
if role == MemoryUsageRole:
return (
node.data.get(ids.CURRENT_MEMORY_USAGE),
node.data.get(ids.MAX_MEMORY_USAGE),
)
return QVariant()

@staticmethod
Expand Down
24 changes: 21 additions & 3 deletions src/ert/gui/simulation/run_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,17 @@
FullSnapshotEvent,
SnapshotUpdateEvent,
)
from ert.ensemble_evaluator import identifiers as ids
from ert.gui.ertnotifier import ErtNotifier
from ert.gui.ertwidgets.message_box import ErtMessageBox
from ert.gui.model.job_list import JobListProxyModel
from ert.gui.model.progress_proxy import ProgressProxyModel
from ert.gui.model.snapshot import FileRole, IterNum, RealIens, SnapshotModel
from ert.gui.model.snapshot import (
FileRole,
IterNum,
RealIens,
SnapshotModel,
)
from ert.gui.tools.file import FileDialog
from ert.gui.tools.plot.plot_tool import PlotTool
from ert.run_models import (
Expand All @@ -42,7 +48,7 @@
RunModelUpdateEndEvent,
)
from ert.run_models.event import RunModelErrorEvent
from ert.shared.status.utils import format_running_time
from ert.shared.status.utils import byte_with_unit, format_running_time

from .queue_emitter import QueueEmitter
from .view import LegendView, ProgressView, RealizationWidget, UpdateWidget
Expand Down Expand Up @@ -123,6 +129,7 @@ def __init__(
self._job_view.verticalHeader().setMinimumWidth(20)

self.running_time = QLabel("")
self.memory_usage = QLabel("")

self.plot_tool = PlotTool(config_file, self.parent())
self.plot_button = QPushButton(self.plot_tool.getName())
Expand Down Expand Up @@ -152,6 +159,8 @@ def __init__(
button_layout.addWidget(self.processing_animation)
button_layout.addWidget(self.running_time)
button_layout.addStretch()
button_layout.addWidget(self.memory_usage)
button_layout.addStretch()
button_layout.addWidget(self.show_details_button)
button_layout.addWidget(self.plot_button)
button_layout.addWidget(self.kill_button)
Expand Down Expand Up @@ -341,6 +350,16 @@ def _on_ticker(self):
runtime = self._run_model.get_runtime()
self.running_time.setText(format_running_time(runtime))

current_memory_usage = self._snapshot_model.root.data.get(
ids.CURRENT_MEMORY_USAGE
)
maximum_memory_usage = self._snapshot_model.root.data.get(ids.MAX_MEMORY_USAGE)

if current_memory_usage and maximum_memory_usage:
self.memory_usage.setText(
f"Memory Usage: [Current: {byte_with_unit(current_memory_usage)}, Max: {byte_with_unit(maximum_memory_usage)}]"
)

@Slot(object)
def _on_event(self, event: object):
if isinstance(event, EndEvent):
Expand All @@ -359,7 +378,6 @@ def _on_event(self, event: object):
total_progress=progress, phase_name=event.phase_name
)
)

elif isinstance(event, SnapshotUpdateEvent):
if event.partial_snapshot is not None:
self._snapshot_model._add_partial_snapshot(
Expand Down
52 changes: 42 additions & 10 deletions src/ert/gui/simulation/view/realization.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
import math
from typing import Final

from qtpy.QtCore import QModelIndex, QRect, QSize, Qt, Signal
from qtpy.QtCore import QEvent, QModelIndex, QPoint, QRect, QSize, Qt, Signal
from qtpy.QtGui import QColor, QColorConstants, QImage, QPainter, QPen
from qtpy.QtWidgets import (
QAbstractItemView,
QListView,
QStyle,
QStyledItemDelegate,
QStyleOptionViewItem,
QToolTip,
QVBoxLayout,
QWidget,
)

from ert.ensemble_evaluator import state
from ert.gui.model.real_list import RealListModel
from ert.gui.model.snapshot import RealJobColorHint, RealLabelHint, RealStatusColorHint
from ert.gui.model.snapshot import (
MemoryUsageRole,
RealJobColorHint,
RealLabelHint,
RealStatusColorHint,
)
from ert.shared.status.utils import byte_with_unit

COLOR_RUNNING: Final[QColor] = QColor(*state.COLOR_RUNNING)
COLOR_FINISHED: Final[QColor] = QColor(*state.COLOR_FINISHED)
Expand All @@ -32,9 +39,11 @@ def __init__(self, _it: int, parent=None) -> None:
self._real_view = QListView(self)
self._real_view.setViewMode(QListView.IconMode)
self._real_view.setGridSize(QSize(self._delegateWidth, self._delegateHeight))
self._real_view.setItemDelegate(
RealizationDelegate(self._delegateWidth, self._delegateHeight, self)
real_delegate = RealizationDelegate(
self._delegateWidth, self._delegateHeight, self
)
self._real_view.setMouseTracking(True)
self._real_view.setItemDelegate(real_delegate)
self._real_view.setSelectionMode(QAbstractItemView.SingleSelection)
self._real_view.setFlow(QListView.LeftToRight)
self._real_view.setWrapping(True)
Expand Down Expand Up @@ -72,12 +81,16 @@ class RealizationDelegate(QStyledItemDelegate):
def __init__(self, width, height, parent=None) -> None:
super().__init__(parent)
self._size = QSize(width, height)
self.parent().installEventFilter(self)
self.job_rect_margin = 10
self.adjustment_point_for_job_rect_margin = QPoint(
-2 * self.job_rect_margin, -2 * self.job_rect_margin
)

def paint(self, painter, option: QStyleOptionViewItem, index: QModelIndex) -> None:
text = index.data(RealLabelHint)
colors = tuple(index.data(RealJobColorHint))
queue_color = index.data(RealStatusColorHint)

painter.save()
painter.setRenderHint(QPainter.Antialiasing, False)
painter.setRenderHint(QPainter.TextAntialiasing, True)
Expand Down Expand Up @@ -106,12 +119,11 @@ def paint(self, painter, option: QStyleOptionViewItem, index: QModelIndex) -> No
painter.setBrush(realization_status_color)
painter.drawRect(realization_status_rect)

job_rect_margin = 10
job_rect = QRect(
option.rect.x() + job_rect_margin,
option.rect.y() + job_rect_margin,
option.rect.width() - (job_rect_margin * 2),
option.rect.height() - (job_rect_margin * 2),
option.rect.x() + self.job_rect_margin,
option.rect.y() + self.job_rect_margin,
option.rect.width() - (self.job_rect_margin * 2),
option.rect.height() - (self.job_rect_margin * 2),
)

if realization_status_color == COLOR_FINISHED:
Expand Down Expand Up @@ -153,3 +165,23 @@ def _paint_inner_grid(painter: QPainter, rect: QRect, colors) -> None:

def sizeHint(self, option, index) -> QSize:
return self._size

def eventFilter(self, watched, event: QEvent):
if event.type() == QEvent.Type.ToolTip:
mouse_pos = event.pos() + self.adjustment_point_for_job_rect_margin
parent: RealizationWidget = self.parent()
view = parent._real_view
index = view.indexAt(mouse_pos)
if index.isValid():
(current_memory_usage, maximum_memory_usage) = index.data(
MemoryUsageRole
)
if current_memory_usage and maximum_memory_usage:
txt = (
f"Current memory usage:\t{byte_with_unit(current_memory_usage)}\n"
f"Maximum memory usage:\t{byte_with_unit(maximum_memory_usage)}"
)
QToolTip.showText(view.mapToGlobal(mouse_pos), txt)
return True

return super().eventFilter(watched, event)

0 comments on commit 004ea8a

Please sign in to comment.