Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## 2024-05-15 - psutil process iteration performance
**Learning:** Querying expensive attributes like `memory_info` and `status` for all processes in `psutil.process_iter` incurs significant overhead, especially on Windows.
**Action:** Only query basic attributes like `pid` and `name` initially, and lazily fetch expensive attributes wrapped in try/except blocks only for the specific target processes.
19 changes: 16 additions & 3 deletions desktop_services/excel_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,17 +206,30 @@ def get_running_instances(self) -> List[Dict[str, Any]]:
def _get_excel_process_snapshots(self) -> Dict[int, Dict[str, Any]]:
"""Find Excel processes with one psutil scan for refresh performance."""
snapshots: Dict[int, Dict[str, Any]] = {}
for proc in psutil.process_iter(["pid", "name", "memory_info", "status"]):
# OPTIMIZATION: Only fetch basic attributes initially to avoid expensive calls for all processes
for proc in psutil.process_iter(["pid", "name"]):
try:
info = proc.info
process_name = info.get("name") or ""
if process_name.lower() != "excel.exe":
continue
pid = int(info["pid"])

# Lazily fetch expensive attributes only for target processes
try:
memory_info = proc.memory_info()
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess, AttributeError):
memory_info = None

try:
status = proc.status()
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess, AttributeError):
status = "running"
Comment on lines +225 to +227
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Skip vanished Excel processes instead of marking running

When proc.status() raises NoSuchProcess/ZombieProcess, this code now falls back to "running" and still records the PID. That creates ghost Excel entries when a process exits between proc.info and the lazy attribute reads, so get_running_instances() can briefly report non-existent sessions with 0.0 MB memory. Previously, a process disappearing during the scan path was skipped by the outer exception handling.

Useful? React with πŸ‘Β / πŸ‘Ž.


snapshots[pid] = {
"name": process_name,
"memory_mb": self._memory_mb_from_info(info.get("memory_info")),
"status": info.get("status") or "running",
"memory_mb": self._memory_mb_from_info(memory_info),
"status": status,
}
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
continue
Expand Down
19 changes: 16 additions & 3 deletions desktop_services/operations_cockpit_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -852,19 +852,32 @@ def _default_processes() -> list[dict[str, Any]]:
if psutil is None:
return []
processes = []
for proc in psutil.process_iter(["pid", "name", "memory_info", "status"]):
# OPTIMIZATION: Only fetch basic attributes initially to avoid expensive calls for all processes
for proc in psutil.process_iter(["pid", "name"]):
try:
info = proc.info
except (psutil.NoSuchProcess, psutil.AccessDenied):
continue
if str(info.get("name") or "").casefold() != "excel.exe":
continue

# Lazily fetch expensive attributes only for target processes
try:
memory_info = proc.memory_info()
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess, AttributeError):
memory_info = None

try:
status = proc.status()
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess, AttributeError):
status = "running"
Comment on lines +871 to +873
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Exclude terminated Excel PIDs from cockpit snapshots

In the cockpit guard, NoSuchProcess/ZombieProcess during lazy status() lookup is converted to "running" and appended anyway. If Excel exits during iteration, _default_processes() can emit stale rows, inflating running_count and downstream risk summaries for that polling cycle. This should skip terminated processes instead of coercing them to running.

Useful? React with πŸ‘Β / πŸ‘Ž.


processes.append(
{
"pid": info.get("pid"),
"name": info.get("name"),
"memory_info": info.get("memory_info"),
"status": info.get("status"),
"memory_info": memory_info,
"status": status,
}
)
return processes
Expand Down
2 changes: 1 addition & 1 deletion tests/test_excel_desktop_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ def status(self):
"is_responsive": True,
}
]
assert requested_fields == [["pid", "name", "memory_info", "status"]]
assert requested_fields == [["pid", "name"]]
assert title_lookup_pids == [{42}]


Expand Down
Loading