Skip to content

Latest commit

 

History

33 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

AEV

A minimal system fetch written in pure Rust for Linux x86_64

⚡ low-level • 🦀 rust • 🖥 clean • 🌙 minimal


✨ About

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, PCI uevent.
  • Optional persistent hardware snapshot to skip the PCI scan on repeat runs.

Designed for low startup overhead; benchmarked on Linux x86_64.


🚀 Installation

Build from source

git clone https://github.com/azytar/aev.git
cd aev
cargo build --release

Run:

./target/release/aev

Arch Linux (AUR)

yay -S aev

🛠 Features

  • 🦀 written in pure Rust, no external crates
  • 🧠 direct CPUID CPU detection (x86_64)
  • 🎮 NVIDIA / AMD / Intel GPU detection via PCI
  • 🧩 built-in gpu_db plus a pci.ids fallback
  • 💾 persistent hardware snapshot (optional, degradable)
  • 🧼 clean single-buffer output with one write
  • 🐧 Linux x86_64 (CPU detection relies on core::arch::x86_64)

🔬 Technical Details

aev avoids slow abstractions wherever it matters:

  • __cpuid() / __cpuid_count() for the CPU brand and topology.
  • Byte-level parsing of /proc and /etc (direct indexing, no read_to_string + String per field).
  • dirfd-relative openat for each PCI uevent (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 String and flushed with a single write, instead of many println! calls.
  • Fat LTO in release builds to shrink code and cut startup instructions / page-faults.

GPU detection

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 uevent interface, 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_db lookup, with a pci.ids fallback.

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.

Hardware snapshot

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 (FNV over 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=1 to force the cold path.
  • The cache is optional and degradable: if XDG_CACHE_HOME/HOME is unavailable, the filesystem is read-only, or /proc/bus/pci is absent, AEV simply runs the baseline scan.

📊 Performance

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 startupexecve + 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.


🧩 Design decisions

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

🌌 Philosophy

simple tools fast binaries low overhead clean terminals


📜 License

MIT


made with ☕ + rust

About

Minimal rust system fetch

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages