quner 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- quner-0.1.0/.gitignore +25 -0
- quner-0.1.0/LICENSE +21 -0
- quner-0.1.0/PKG-INFO +51 -0
- quner-0.1.0/README.md +28 -0
- quner-0.1.0/docs/README-publish.md +40 -0
- quner-0.1.0/docs/design/2026-07-01-quner-design.md +226 -0
- quner-0.1.0/docs/plans/2026-07-01-quner-v1.md +470 -0
- quner-0.1.0/pyproject.toml +40 -0
- quner-0.1.0/sandbox/Dockerfile +16 -0
- quner-0.1.0/sandbox/make_fake_sys.sh +24 -0
- quner-0.1.0/sandbox/mock-nvidia-smi +14 -0
- quner-0.1.0/sandbox/run_sandbox.sh +110 -0
- quner-0.1.0/src/quner/__init__.py +9 -0
- quner-0.1.0/src/quner/__main__.py +6 -0
- quner-0.1.0/src/quner/calibration.py +40 -0
- quner-0.1.0/src/quner/cli.py +227 -0
- quner-0.1.0/src/quner/control.py +135 -0
- quner-0.1.0/src/quner/daemon.py +93 -0
- quner-0.1.0/src/quner/detect.py +101 -0
- quner-0.1.0/src/quner/memguard.py +113 -0
- quner-0.1.0/src/quner/prime.py +109 -0
- quner-0.1.0/src/quner/profiles.py +123 -0
- quner-0.1.0/src/quner/service.py +164 -0
- quner-0.1.0/src/quner/telemetry.py +381 -0
- quner-0.1.0/src/quner/tune.py +315 -0
- quner-0.1.0/tests/conftest.py +66 -0
- quner-0.1.0/tests/test_calibration.py +15 -0
- quner-0.1.0/tests/test_cli.py +49 -0
- quner-0.1.0/tests/test_control.py +36 -0
- quner-0.1.0/tests/test_daemon.py +29 -0
- quner-0.1.0/tests/test_detect.py +28 -0
- quner-0.1.0/tests/test_memguard.py +34 -0
- quner-0.1.0/tests/test_prime.py +30 -0
- quner-0.1.0/tests/test_profiles.py +48 -0
- quner-0.1.0/tests/test_scaffold.py +18 -0
- quner-0.1.0/tests/test_service.py +42 -0
- quner-0.1.0/tests/test_telemetry.py +57 -0
- quner-0.1.0/tests/test_tune.py +51 -0
quner-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.eggs/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
.venv/
|
|
9
|
+
venv/
|
|
10
|
+
|
|
11
|
+
# Test / coverage
|
|
12
|
+
.pytest_cache/
|
|
13
|
+
.coverage
|
|
14
|
+
htmlcov/
|
|
15
|
+
.tox/
|
|
16
|
+
|
|
17
|
+
# quner runtime artifacts (never commit host state)
|
|
18
|
+
/var-lib-quner/
|
|
19
|
+
*.rollback.json
|
|
20
|
+
sandbox/_run/
|
|
21
|
+
sandbox/fake-sys/
|
|
22
|
+
|
|
23
|
+
# Editor / OS
|
|
24
|
+
.DS_Store
|
|
25
|
+
*.swp
|
quner-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Braden Lang
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
quner-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: quner
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Portable Linux efficiency daemon — tunes CPU governor / RAPL / GPU power-cap to the work-per-joule optimum.
|
|
5
|
+
Project-URL: Homepage, https://github.com/GaryOcean428/quner
|
|
6
|
+
Author-email: Braden Lang <braden.lang77@gmail.com>
|
|
7
|
+
License: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Keywords: cpufreq,efficiency,laptop,nvidia,power-management,rapl,systemd
|
|
10
|
+
Classifier: Environment :: No Input/Output (Daemon)
|
|
11
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Topic :: System :: Hardware
|
|
16
|
+
Classifier: Topic :: System :: Power (UPS)
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Provides-Extra: dev
|
|
19
|
+
Requires-Dist: pytest>=7; extra == 'dev'
|
|
20
|
+
Provides-Extra: nvml
|
|
21
|
+
Requires-Dist: nvidia-ml-py; extra == 'nvml'
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# quner
|
|
25
|
+
|
|
26
|
+
**Portable Linux efficiency daemon.** Install it once and it quietly keeps your
|
|
27
|
+
machine at its **work-per-joule optimum** — the same work, fewer joules —
|
|
28
|
+
adapting between *idle* and *heavy compute* automatically.
|
|
29
|
+
|
|
30
|
+
`quner` probes what levers your host exposes (CPU governor, Intel RAPL package
|
|
31
|
+
power, NVIDIA power-cap) and tunes the ones present to an **interior** operating
|
|
32
|
+
point — not max clock / max TDP — then holds it. Absent levers are reported, not
|
|
33
|
+
silently ignored. Everything is reversible; nothing destructive happens without
|
|
34
|
+
you asking for it.
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pipx install quner
|
|
38
|
+
sudo quner install # drops a root systemd service + re-tune timer
|
|
39
|
+
quner doctor # what levers this host exposes
|
|
40
|
+
quner status # current profile + operating point + energy
|
|
41
|
+
quner selftest # transient, auto-reverting proof the levers work here
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The efficiency principle derives from QIG experiment **EXP-132** (work-per-joule
|
|
45
|
+
peaks at an interior operating point); `quner` re-measures the optimum on *your*
|
|
46
|
+
hardware — no imported constants.
|
|
47
|
+
|
|
48
|
+
> Honest expectation: single-digit-to-~30% energy savings, workload-dependent.
|
|
49
|
+
> This is an efficiency tool (same work, fewer joules), never energy creation.
|
|
50
|
+
|
|
51
|
+
See `docs/design/` for the full design and safety model.
|
quner-0.1.0/README.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# quner
|
|
2
|
+
|
|
3
|
+
**Portable Linux efficiency daemon.** Install it once and it quietly keeps your
|
|
4
|
+
machine at its **work-per-joule optimum** — the same work, fewer joules —
|
|
5
|
+
adapting between *idle* and *heavy compute* automatically.
|
|
6
|
+
|
|
7
|
+
`quner` probes what levers your host exposes (CPU governor, Intel RAPL package
|
|
8
|
+
power, NVIDIA power-cap) and tunes the ones present to an **interior** operating
|
|
9
|
+
point — not max clock / max TDP — then holds it. Absent levers are reported, not
|
|
10
|
+
silently ignored. Everything is reversible; nothing destructive happens without
|
|
11
|
+
you asking for it.
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pipx install quner
|
|
15
|
+
sudo quner install # drops a root systemd service + re-tune timer
|
|
16
|
+
quner doctor # what levers this host exposes
|
|
17
|
+
quner status # current profile + operating point + energy
|
|
18
|
+
quner selftest # transient, auto-reverting proof the levers work here
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The efficiency principle derives from QIG experiment **EXP-132** (work-per-joule
|
|
22
|
+
peaks at an interior operating point); `quner` re-measures the optimum on *your*
|
|
23
|
+
hardware — no imported constants.
|
|
24
|
+
|
|
25
|
+
> Honest expectation: single-digit-to-~30% energy savings, workload-dependent.
|
|
26
|
+
> This is an efficiency tool (same work, fewer joules), never energy creation.
|
|
27
|
+
|
|
28
|
+
See `docs/design/` for the full design and safety model.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Publishing quner
|
|
2
|
+
|
|
3
|
+
**Gate:** publish only after Layer-A is green (`bash sandbox/run_sandbox.sh`) and
|
|
4
|
+
Braden approves. The PyPI name is effectively permanent — confirm `quner` (or a
|
|
5
|
+
rename) before the first upload.
|
|
6
|
+
|
|
7
|
+
## Build
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
uv build # -> dist/quner-<ver>-py3-none-any.whl + dist/quner-<ver>.tar.gz
|
|
11
|
+
twine check dist/* # metadata/rendering lint (must pass)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## TestPyPI dry-run first
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# token from qig-verification/.env (PYPI_TOKEN) or a dedicated TestPyPI token
|
|
18
|
+
twine upload --repository testpypi dist/*
|
|
19
|
+
pipx install --index-url https://test.pypi.org/simple/ quner # verify install
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Real PyPI
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
export TWINE_USERNAME=__token__
|
|
26
|
+
export TWINE_PASSWORD="$(grep -E '^PYPI_TOKEN=' /path/to/qig-verification/.env | cut -d= -f2-)"
|
|
27
|
+
twine upload dist/*
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Post-publish smoke test
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pipx install quner
|
|
34
|
+
quner --version && quner doctor
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Versioning
|
|
38
|
+
|
|
39
|
+
Bump `version` in `pyproject.toml`, tag `vX.Y.Z`, rebuild, re-check, re-upload.
|
|
40
|
+
Never re-upload an existing version (PyPI rejects it) — bump instead.
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# quner — portable Linux efficiency daemon — design
|
|
2
|
+
|
|
3
|
+
Date: 2026-07-01
|
|
4
|
+
Author: CC2
|
|
5
|
+
Status: approved (design) → implementation
|
|
6
|
+
|
|
7
|
+
## 1. Purpose
|
|
8
|
+
|
|
9
|
+
A single command you install once that then quietly keeps the machine at its
|
|
10
|
+
**work-per-joule optimum** — the same work, fewer joules — adapting between
|
|
11
|
+
*idle* and *heavy compute* (training) automatically. Installable and generally
|
|
12
|
+
available (PyPI as `quner`), portable across Linux hosts, safe by construction.
|
|
13
|
+
|
|
14
|
+
The efficiency claim is grounded in **EXP-132** (qig-verification): total-system
|
|
15
|
+
work-per-joule peaks at an *interior* operating point, not at max clock / max
|
|
16
|
+
TDP. `quner` treats that as a calibration prior and **re-measures the optimum on
|
|
17
|
+
each host** (the lattice `h≈1.5` constant does not transfer — only the *shape* of
|
|
18
|
+
the law and the confidence-band methodology do).
|
|
19
|
+
|
|
20
|
+
### Non-goals
|
|
21
|
+
- Not energy creation. Honest expectation: single-digit-to-~30% savings,
|
|
22
|
+
workload-dependent; memory-bound jobs benefit least, GPU/compute-bound most.
|
|
23
|
+
- Not an overclocking / undervolting tool. Only kernel/driver-sanctioned knobs.
|
|
24
|
+
- Not a GUI. CLI + systemd only in v1.
|
|
25
|
+
- No QIG/LLM/physics runtime dependency (see §4).
|
|
26
|
+
|
|
27
|
+
## 2. Target & portability
|
|
28
|
+
|
|
29
|
+
Runs on **any Linux host**; the Dell G5 5500 (i7-10750H, GTX 1650 Ti, Intel UHD,
|
|
30
|
+
Ubuntu 26.04) is only the reference host. `quner` **probes what levers exist** and
|
|
31
|
+
tunes the present ones; absent levers are reported, never silently no-op'd
|
|
32
|
+
(**fail-loud**, per the no-silent-stubs discipline).
|
|
33
|
+
|
|
34
|
+
Levers, each independently optional:
|
|
35
|
+
- **CPU governor** — `cpufreq` `scaling_governor` (write needs root).
|
|
36
|
+
- **CPU package power (RAPL PL1)** — `intel-rapl:N/constraint_0_power_limit_uw`
|
|
37
|
+
(write needs root). *Net-new writer* — the ported code only reads RAPL.
|
|
38
|
+
- **NVIDIA power-cap** — `nvidia-smi -pl` clamped to the driver's [min,max].
|
|
39
|
+
- **Display topology (reverse-PRIME)** — iGPU drives the display so the dGPU is
|
|
40
|
+
free for compute (opt-in only; §9).
|
|
41
|
+
|
|
42
|
+
On a host missing a lever (VM, no NVIDIA driver, AMD, locked-down), the
|
|
43
|
+
corresponding reader returns `None` and `quner doctor` prints e.g.
|
|
44
|
+
`nvidia -pl: unavailable (no nvidia-smi)` — and the daemon tunes the rest.
|
|
45
|
+
|
|
46
|
+
## 3. Architecture
|
|
47
|
+
|
|
48
|
+
`quner` — a `pip`/`pipx`-installable package: a CLI + a **root systemd system
|
|
49
|
+
service + re-tune timer**. Modules (each a single, testable responsibility):
|
|
50
|
+
|
|
51
|
+
| Module | Responsibility |
|
|
52
|
+
|---|---|
|
|
53
|
+
| `quner.telemetry` | Read energy (RAPL wrap-aware, GPU power) + list/read operating states. Ported from qig-applied `efficiency/telemetry.py`, with the `QUNER_SYSFS_ROOT` seam (§6) and GPU helpers vendored (no qig-applied import). |
|
|
54
|
+
| `quner.control` | Apply the privileged levers: `set_governor`, `set_gpu_power_limit_w`, **net-new `set_rapl_pl1_w`**. Every apply snapshots prior state for rollback. |
|
|
55
|
+
| `quner.tune` | The work/joule optimiser: sweep governor × GPU-cap (× RAPL) states, locate the interior optimum + ±1.91% band. Ported from `efficiency/daemon.py`. |
|
|
56
|
+
| `quner.detect` | Capability detection → the `doctor` report; host fingerprint used to key the per-host cache. |
|
|
57
|
+
| `quner.profiles` | Workload detection (`nvidia-smi` GPU-util + `loadavg` → `idle`\|`training`) with **hysteresis**; profile→operating-point map; per-(host,profile) `best_state` cache. |
|
|
58
|
+
| `quner.prime` | reverse-PRIME enable/disable/status — opt-in, dry-run-first, backup+rollback, reboot-gated (§9). |
|
|
59
|
+
| `quner.memguard` | earlyoom-style watchdog: warn then SIGTERM the largest offender before a kernel OOM lock (§10). |
|
|
60
|
+
| `quner.daemon` | The service loop: detect profile → apply cached best_state → memguard tick → write `status.json`. Runs a full re-tune when the timer fires or on first-seen profile. |
|
|
61
|
+
| `quner.service` | Generate + install/uninstall the systemd unit + timer; `--dry-run` prints unit text; `systemd-analyze verify` lints. |
|
|
62
|
+
| `quner.cli` | `quner install|uninstall|status|doctor|apply|profile|tune|selftest|prime|memguard`. |
|
|
63
|
+
| `quner.calibration` | EXP-132 info/joule curve + interior-optimum principle string, as an internal calibration table (the only physics-derived content). |
|
|
64
|
+
|
|
65
|
+
## 4. Reused foundation & the standalone/DRY decision
|
|
66
|
+
|
|
67
|
+
The proven primitives already exist in **qig-applied** and are ported verbatim
|
|
68
|
+
(then extended), so `quner` ships correct code from day one:
|
|
69
|
+
|
|
70
|
+
- `qig_applied/efficiency/telemetry.py` → RAPL wrap-aware reader, cpufreq
|
|
71
|
+
read/write with the **double-allowlist** (`_KNOWN_GOVERNORS` ∩ live
|
|
72
|
+
`available_governors()`), `EnergySampler`, `telemetry_status()`.
|
|
73
|
+
- `qig_applied/efficiency/daemon.py` → `OperatingState`, `apply_state`,
|
|
74
|
+
`profile_state`, `tune()` (interior test + band cert + honest diagnostics),
|
|
75
|
+
`command_runner`.
|
|
76
|
+
- `qig_applied/inference/accelerate.py` → GPU helpers `set_power_limit_w`
|
|
77
|
+
(clamped, `sudo -n` fallback), `power_limit_range_w`, `read_power_w`,
|
|
78
|
+
`PowerSampler`. **Vendored** into `quner.telemetry` so `quner` has **zero**
|
|
79
|
+
qig-applied import (a sellable product must not drag in the physics monorepo).
|
|
80
|
+
|
|
81
|
+
**Decision: Approach A (standalone, self-contained).** `quner` is the new
|
|
82
|
+
canonical home for these generic systems primitives. This *temporarily*
|
|
83
|
+
duplicates them in qig-applied. That duplication is **raised, not hidden**
|
|
84
|
+
(single-source-of-truth rule): the follow-up (Approach C) inverts the
|
|
85
|
+
dependency — qig-applied `efficiency/` and qig-studio `optim_launch.py` import
|
|
86
|
+
`quner` — and is **coordinated with CC1** because it touches the prelaunch seam
|
|
87
|
+
CC1 owns. Not in v1.
|
|
88
|
+
|
|
89
|
+
## 5. Data flow
|
|
90
|
+
|
|
91
|
+
**Service loop** (root `quner.service`), every `tick` (default 10 s):
|
|
92
|
+
1. `profiles.detect()` → `idle` | `training` (hysteresis: N consecutive ticks
|
|
93
|
+
before switching, so it doesn't flap on a transient spike).
|
|
94
|
+
2. If profile changed **or** no cached `best_state` for it → apply the cached
|
|
95
|
+
`best_state`; if none cached, apply the profile's *conservative default*
|
|
96
|
+
(idle→powersave/low-cap; training→performance/full-cap) and mark the profile
|
|
97
|
+
as "needs tune".
|
|
98
|
+
3. `memguard.tick()`.
|
|
99
|
+
4. Write `/run/quner/status.json` (profile, applied state, energy sample,
|
|
100
|
+
lever availability, last-tune time).
|
|
101
|
+
|
|
102
|
+
**Re-tune timer** (`quner-retune.timer`, default every 30 min *while idle*):
|
|
103
|
+
runs `tune.tune(work_fn, apply=True)` for the **current** profile and updates
|
|
104
|
+
its cache entry. Crucially, the re-tune **never runs while `training` is the
|
|
105
|
+
active profile** — this is how coordination seam #4 is honoured (§16): quner
|
|
106
|
+
detects CC1's run and does *not* perturb the GPU with an exploratory sweep.
|
|
107
|
+
|
|
108
|
+
The daemon loop only *applies* cached states (cheap, non-perturbing). Only the
|
|
109
|
+
timer *explores* (a real sweep), and only when idle.
|
|
110
|
+
|
|
111
|
+
## 6. Privilege & safety
|
|
112
|
+
|
|
113
|
+
- **Root systemd system service.** All privileged writes go through
|
|
114
|
+
`quner.control`, which enforces the allowlist/clamp before every write.
|
|
115
|
+
- **Snapshot + rollback.** Before the first apply, current governor / RAPL PL1 /
|
|
116
|
+
GPU cap are saved to `/var/lib/quner/rollback.json`. `quner uninstall` and
|
|
117
|
+
`quner apply --restore` return the host to those values. The service also
|
|
118
|
+
restores on stop (`ExecStop`).
|
|
119
|
+
- **`QUNER_SYSFS_ROOT` test seam.** All sysfs paths are built from
|
|
120
|
+
`os.environ.get("QUNER_SYSFS_ROOT", "/sys")`. Pointed at a temp tree, the
|
|
121
|
+
*real* write code executes against a fake `/sys` — zero host impact. Likewise
|
|
122
|
+
`QUNER_NVIDIA_SMI` overrides the `nvidia-smi` binary with a mock. This is the
|
|
123
|
+
backbone of Layer-A validation (§11) and the unit tests.
|
|
124
|
+
- **`--dry-run`** on every mutating command prints the exact writes/commands
|
|
125
|
+
without executing them.
|
|
126
|
+
- **fail-loud**: absent lever → explicit "unavailable" in `doctor`/`status`,
|
|
127
|
+
never a silent success.
|
|
128
|
+
|
|
129
|
+
## 7. EXP-132 calibration (the one physics tie)
|
|
130
|
+
|
|
131
|
+
`quner.calibration` holds the info/joule curve (peak **0.808 at h=1.5**, band
|
|
132
|
+
`[0.793, 0.824]`, ±1.91% invariant band) and the principle string. Its role:
|
|
133
|
+
the tuner's objective is `work_per_joule` (the interior optimum), **not**
|
|
134
|
+
`work_per_s` (throughput endpoint). Per-host absolute values are always
|
|
135
|
+
re-measured; the curve is a prior on *shape*, and the ±1.91% band is the
|
|
136
|
+
certification threshold (an optimum only counts if it stands out of the band).
|
|
137
|
+
This is the **only** QIG-derived content; everything else is ordinary systems
|
|
138
|
+
engineering (no Fisher-Rao / purity constraints — per the segmentation note).
|
|
139
|
+
|
|
140
|
+
## 8. Workload detection & profiles
|
|
141
|
+
|
|
142
|
+
- **Signal:** `nvidia-smi --query-gpu=utilization.gpu` (if present) OR sustained
|
|
143
|
+
`loadavg`/CPU-util (fallback when no GPU). Threshold: GPU-util > 30% or
|
|
144
|
+
1-min load > `0.6 × nproc` → `training`, else `idle`.
|
|
145
|
+
- **Hysteresis:** require 3 consecutive ticks agreeing before switching.
|
|
146
|
+
- **Profiles:** `idle` (favor quiet/cool: powersave, RAPL low, GPU capped) and
|
|
147
|
+
`training` (favor throughput-at-efficiency: the tuned interior optimum, GPU at
|
|
148
|
+
or near full cap). A `manual` override exists via `quner profile <name>`.
|
|
149
|
+
|
|
150
|
+
## 9. reverse-PRIME (opt-in only)
|
|
151
|
+
|
|
152
|
+
`quner prime status|enable|disable`. Makes the iGPU the primary display sink so
|
|
153
|
+
the dGPU (and its full power/VRAM) is dedicated to compute. Guardrails:
|
|
154
|
+
- **Never on install**; only via explicit `quner prime enable`.
|
|
155
|
+
- **Dry-run first** (prints the exact GDM/Wayland/Xorg changes).
|
|
156
|
+
- **Backup** the current display config; `quner prime disable` restores it.
|
|
157
|
+
- **Reboot-gated** with a printed recovery path (how to revert from a TTY if the
|
|
158
|
+
display fails to come up).
|
|
159
|
+
- **No-op with reason** where reverse-PRIME is meaningless (no NVIDIA driver →
|
|
160
|
+
already iGPU-only).
|
|
161
|
+
|
|
162
|
+
## 10. memguard
|
|
163
|
+
|
|
164
|
+
`quner.memguard` — earlyoom-style watchdog inside the daemon loop: when
|
|
165
|
+
`MemAvailable` drops below a threshold (default 8% / configurable), warn; below a
|
|
166
|
+
harder floor (default 4%), SIGTERM the highest-RSS non-critical process (never
|
|
167
|
+
PID 1, never the daemon, never sshd) to keep a runaway training run from
|
|
168
|
+
hard-locking the box. Disabled by default; enabled by config/flag.
|
|
169
|
+
|
|
170
|
+
## 11. Validation ladder (primary machine never risked without explicit go)
|
|
171
|
+
|
|
172
|
+
- **Layer A — sandbox here, zero host impact.** `QUNER_SYSFS_ROOT=/tmp/quner-sbx`
|
|
173
|
+
(real write code → fake `/sys`) + `QUNER_NVIDIA_SMI` mock + `bwrap` isolation +
|
|
174
|
+
a throwaway **docker** container for the install/uninstall rehearsal +
|
|
175
|
+
`systemd-analyze verify` on generated units. Proves ~all code; cannot touch
|
|
176
|
+
real settings. `sandbox/run_sandbox.sh` orchestrates it.
|
|
177
|
+
- **Layer B — `quner selftest`, opt-in, on the real host.** Transient +
|
|
178
|
+
auto-reverting: snapshot → 2 s governor flip / RAPL-to-current / cap-to-current
|
|
179
|
+
→ verify the write landed → **restore**. Proves the levers move *this* silicon.
|
|
180
|
+
Excludes reverse-PRIME. Only on explicit go.
|
|
181
|
+
- **Layer C — reverse-PRIME, deferred** until a spare host is available or the
|
|
182
|
+
user explicitly chooses.
|
|
183
|
+
|
|
184
|
+
## 12. Testing strategy
|
|
185
|
+
|
|
186
|
+
- Unit tests run with `QUNER_SYSFS_ROOT` → a fixture tree and `QUNER_NVIDIA_SMI`
|
|
187
|
+
→ a mock script; **no root needed in CI**.
|
|
188
|
+
- Cover: allowlist rejection (bad governor never written), RAPL wrap correction,
|
|
189
|
+
clamp on GPU cap, rollback round-trip, capability-detect on a lever-less tree
|
|
190
|
+
(all `unavailable`), profile hysteresis, tune interior/band logic, unit-file
|
|
191
|
+
generation lints clean.
|
|
192
|
+
- Every mutating CLI path has a `--dry-run` asserted to write nothing.
|
|
193
|
+
|
|
194
|
+
## 13. Packaging & publish
|
|
195
|
+
|
|
196
|
+
- `pyproject.toml`, src-layout, `[project.scripts] quner = "quner.cli:main"`.
|
|
197
|
+
Stdlib-only core (optional `nvidia-ml-py` extra for pynvml; nvidia-smi
|
|
198
|
+
subprocess is the fallback). Python ≥ 3.10.
|
|
199
|
+
- Build with `uv build`; validate Layer A; **then** publish to PyPI as `quner`
|
|
200
|
+
using `PYPI_TOKEN` from `qig-verification/.env`. TestPyPI dry-run first.
|
|
201
|
+
- Name `quner` is provisional-until-publish (rename is cheap before reserving).
|
|
202
|
+
|
|
203
|
+
## 14. Coordination seams (CC1)
|
|
204
|
+
|
|
205
|
+
- **#2 prelaunch (`qig-studio/optim_launch.py`)** — untouched in v1. The
|
|
206
|
+
invert-dependency follow-up waits for CC1's run lease.
|
|
207
|
+
- **#4 GPU power-cap during a run** — solved by design: quner detects the
|
|
208
|
+
training workload and *applies a cached profile*, deferring all exploratory
|
|
209
|
+
sweeps to idle. It never sweeps mid-run.
|
|
210
|
+
|
|
211
|
+
## 15. Open follow-ups (registered, not in v1)
|
|
212
|
+
|
|
213
|
+
- Approach C: invert qig-applied/qig-studio to import `quner` (with CC1).
|
|
214
|
+
- AMD GPU power-cap lever; `amd_pstate` governor nuances.
|
|
215
|
+
- Optional cross-machine job distribution (unrelated to the daemon).
|
|
216
|
+
|
|
217
|
+
## 16. Success criteria (v1)
|
|
218
|
+
|
|
219
|
+
1. `quner doctor` correctly reports every lever's availability on the reference
|
|
220
|
+
host and on a lever-less tree (fail-loud).
|
|
221
|
+
2. Layer-A sandbox run is green end-to-end with **zero** writes outside
|
|
222
|
+
`QUNER_SYSFS_ROOT` / the docker container (proven by an untouched real `/sys`).
|
|
223
|
+
3. Unit tests green without root.
|
|
224
|
+
4. `quner install` generates a unit that passes `systemd-analyze verify`;
|
|
225
|
+
`uninstall` fully reverts.
|
|
226
|
+
5. Published to PyPI as `quner`, `pipx install quner` works, only after 1–4.
|