A minimal system fetch written in pure Rust for Linux x86_64
⚡ low-level • 🦀 rust • 🖥 clean • 🌙 minimal
aev is a lightweight system fetch with a small, dependency-free implementation and a focus on low startup overhead.
- Zero third-party dependencies (pure
std+ the system libc Rust already links). - Single static-ish executable; no subprocesses spawned.
- Direct hardware inspection: CPUID,
/proc,/sys, PCIuevent. - Optional persistent hardware snapshot to skip the PCI scan on repeat runs.
Designed for low startup overhead; benchmarked on Linux x86_64.
git clone https://github.com/azytar/aev.git
cd aev
cargo build --releaseRun:
./target/release/aevyay -S aev- 🦀 written in pure Rust, no external crates
- 🧠 direct
CPUIDCPU detection (x86_64) - 🎮 NVIDIA / AMD / Intel GPU detection via PCI
- 🧩 built-in
gpu_dbplus apci.idsfallback - 💾 persistent hardware snapshot (optional, degradable)
- 🧼 clean single-buffer output with one
write - 🐧 Linux x86_64 (CPU detection relies on
core::arch::x86_64)
aev avoids slow abstractions wherever it matters:
__cpuid()/__cpuid_count()for the CPU brand and topology.- Byte-level parsing of
/procand/etc(direct indexing, noread_to_string+Stringper field). - dirfd-relative
openatfor each PCIuevent(openat(dirfd, "XXX/uevent")) — a shorter kernel path walk and no per-open heap allocation. - Single output buffer: the whole screen is assembled into one
Stringand flushed with a singlewrite, instead of manyprintln!calls. - Fat LTO in release builds to shrink code and cut startup instructions / page-faults.
AEV enumerates PCI display controllers by scanning uevent files under
/sys/bus/pci/devices. It:
- works without a loaded DRM driver (it does not need
/sys/class/drm); - uses the PCI
ueventinterface, not DRM; - does not detect which GPU is currently active / in use;
- does not implement PRIME or NVIDIA Optimus selection;
- resolves names via the built-in
gpu_dblookup, with apci.idsfallback.
During development we measured why DRM enumeration is not equivalent: a
driverless GPU gets no /sys/class/drm/cardN entry, so a DRM-only scan would
miss it. The uevent scan is complete and driver-independent.
On supported systems AEV persists a tiny hardware snapshot
(36 bytes, schema version 2) under $XDG_CACHE_HOME/aev/hardware.snap
(falling back to $HOME/.cache/aev).
- Cached: GPU physical identity (vendor/device) only.
- Not cached: RAM, uptime, shell, hostname, kernel, CPU brand and distro — these are re-read live every run because they are either genuinely dynamic or already cheap to obtain.
- Validation: a fingerprint (
FNVover the sorted full PCI topology from/proc/bus/pci/devices) is recomputed on every run and compared with the stored value. Any change — GPU added, removed, swapped in the same slot, or any PCI topology change — invalidates the snapshot. - An invalid or corrupt snapshot is silently discarded and AEV falls back to a normal PCI scan. It never panics and never trusts unverified data.
- Set
AEV_NO_CACHE=1to force the cold path. - The cache is optional and degradable: if
XDG_CACHE_HOME/HOMEis unavailable, the filesystem is read-only, or/proc/bus/pciis absent, AEV simply runs the baseline scan.
Benchmarked on a reference machine:
- OS: Arch Linux x86_64, kernel
7.1.8-arch1-3 - CPU: Intel Core i5-7300HQ @ 2.50 GHz (4 cores / 4 threads)
- Build:
cargo build --release(fat LTO)
Command:
hyperfine --warmup 5 --runs 40 --shell=none './target/release/aev'| Scenario | mean | median | min | max | uevent opens |
|---|---|---|---|---|---|
| Cold (PCI scan) | 2.21 ms | 1.90 ms | 1.11 ms | 6.12 ms | 22 |
| Cache hit | 2.25 ms | 1.65 ms | 1.04 ms | 7.51 ms | 0 |
No cache (AEV_NO_CACHE=1) |
2.05 ms | 1.78 ms | 1.09 ms | 4.09 ms | 22 |
How to read these numbers. AEV's own logic on the cache-hit path is
~135 µs (measured in-process: ~67 µs for the PCI-topology fingerprint,
~19 µs shell, ~11 µs RAM, the rest < 7 µs each). The observed ~1–2 ms
wall time is dominated by Linux process startup — execve + the dynamic
loader + libc — which on this machine is itself ~0.9 ms (/bin/true alone
is ~0.9 ms). The remaining spread is scheduler variance, not AEV work.
Conclusion: the cache-hit path is already close to the practical floor of this
single-shot execution model. The one meaningful extra cost on a cold run is
the 22× PCI uevent scan (~300 µs of AEV logic), which the snapshot
eliminates on repeat runs.
| Area | Decision | Why |
|---|---|---|
| Process-local CPU/GPU cache | removed | atomic + RwLock overhead exceeded the few-µs it saved |
| sysfs string parsing | byte-level | avoids a String/Vec allocation per field |
/proc parsing |
byte-level | direct indexing, no read_to_string |
| GPU worker thread | removed | clone3+futex cost is invisible next to PCI I/O; adds cycles |
| LTO | enabled | smaller binary, fewer startup instructions/page-faults |
| PGO | rejected | no stable reproducible profile; build complexity > gain |
| DRM enumeration | rejected | driverless GPUs are missed; uevent is complete |
uevent scan |
adopted | one openat+read per device, no full DRM walk |
dirfd / openat |
adopted | shorter kernel path walk, no per-open heap alloc |
| Persistent snapshot | adopted | skips 22×uevent on repeat runs (~135 µs vs ~300 µs) |
machine-id fingerprint |
removed | redundant: the PCI-topology hash already covers hardware identity |
| Output construction | single buffer + write | collapses ~17 allocs/print! into 1 String + 1 write |
simple tools fast binaries low overhead clean terminals
MIT
made with ☕ + rust