# elphel-perfd — CPU+GPU performance management without sudo

Lets Eyesis/imagej-elphel (and any other local program) request deterministic
or maximum-performance clock states with **no sudo in the control path**.
Design ruled by Andrey 07/17/2026: the capability ships with imagej-elphel
because it follows the hardware the project runs on (224/137), while staying
usable by any program through the same local socket.

## Install (once per machine, the only sudo ever needed)

    sudo <imagej-elphel>/perfd/install.sh

Installs `/usr/local/lib/elphel-perfd/elphel_perfd.py`, the systemd unit
`elphel-perfd.service` (enabled, running as root), and the unprivileged
client `/usr/local/bin/perfctl`. Idempotent — re-run to update.

Eyesis checks for the daemon at startup: uses it when present, prints the
install command above when absent (and continues normally).

## Use

    perfctl status                     # clocks/holds one-liner
    perfctl hold measure -- <command>  # deterministic clocks for the command
    perfctl hold max                   # production profile until Ctrl-C

Profiles:
- `measure` — CPU `performance` + GPU persistence + clocks **locked**
  (default 2550 MHz, `PERFD_GPU_LOCK_MHZ` in the unit): run-to-run timing
  reproducibility (±1% instead of ±10% with free-running boost) for per-item
  performance evaluation.
- `max` — CPU `performance` + GPU persistence, clocks unlocked: throughput.

A hold is tied to the client's connection — process exit or crash releases
it automatically (refcounted across concurrent holders; `measure` outranks
`max`). Daemon stop resets GPU clocks (`ExecStopPost=nvidia-smi -rgc`).

Protocol (127.0.0.1:48890, line-based): `PING`, `STATUS`, `HOLD measure|max`,
`RELEASE`, `QUIT`. Java client: `com.elphel.imagej.common.PerfDaemon`.

Supersedes `attic/elphel-agent-tools/bin/cpu_perf_hold.py` (CPU-only,
process-watch based) once installed.
