---
title: "Hardware Detection and Validation"
author: "Stefano Cacciatore"
date: "`r Sys.Date()`"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Hardware Detection and Validation}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
```

`gpuinfo` detects CPU and GPU hardware and reports whether local compute
backends appear usable. Detection is framework-independent: a positive result
does not imply that torch, TensorFlow, or another package was compiled with
that backend.

## Quick check on the current computer

```{r quick-check, eval=FALSE}
library(gpuinfo)

has_gpu()
has_cuda()
has_metal()
has_rocm()
has_opencl()

hardware_info()
gpu_sitrep()
```

CPU and RAM are available independently of GPU backends:

```{r host-resources, eval=FALSE}
cpu_info()
system_memory_info()
hardware_info()$gpu_summary
```

`available_cores` and effective-memory fields account for detectable process,
container, and scheduler limits. They remain separate from host totals. A
multi-GPU flag means multiple devices are visible; it does not prove that
another R package can distribute work across them.

All probes are defensive. Missing hardware, commands, drivers, or libraries
produce `FALSE`, empty results, or `NA` fields instead of an installation or
runtime error. Detailed backend information distinguishes `available`,
`unavailable`, and `unknown`; convenience predicates only return `TRUE` for a
confirmed usable backend.

## Benchmark memory and manifests

Version 0.2.0 separates device-wide memory from process-level memory:

```{r benchmark-memory, eval=FALSE}
device_memory <- gpu_memory_info()
before <- gpu_memory_sample()

# Run GPU work here. Synchronize with the framework before sampling if the
# operation is asynchronous.

after <- gpu_memory_sample()
manifest <- hardware_manifest(memory = "process")

# For worker-based benchmarks, collect several snapshots and aggregate them.
devices <- subset(gpu_devices(), process_visible)
peak <- gpu_memory_peak(list(before, after), devices)
```

`gpu_memory_info()` reports the device-wide total, used, and free values that
the available system probe exposes. `gpu_memory_sample()` instead produces one
timestamped row per process-visible device. Repeated calls can be combined into
a benchmark time series; the function does not run a background thread and
does not force asynchronous GPU work to finish.

For CUDA, memory is the value in `nvidia-smi`'s compute-process table for the
requested PID, reconciled to the CUDA-visible device by UUID. It is current,
driver-attributed allocation rather than a peak. Windows WDDM drivers may not
expose it, in which case the result is `NA` with a reason code. A successful
query with no row for the process is zero bytes and `not_active`.

`gpu_memory_peak()` groups rows by UUID, sums worker processes within each
sampling instant, and returns the largest complete observed sum per expected
device. It never reconciles by logical or physical device number. Missing or
incomplete devices remain `NA` with status `unmeasured`, rather than being
interpreted as zero. The result is sampling-dependent and is not a lifetime
high-water mark maintained by the driver.

For Metal, memory is `MTLDevice.currentAllocatedSize` for the current R process.
It measures current Metal resource allocations, not resident memory, peak
memory, total unified memory, or allocations by other processes. The Metal API
used here cannot inspect an arbitrary PID. Unsupported measurements remain
`NA`; `gpuinfo` never estimates them from total memory.

`hardware_manifest()` packages CPU topology, host/effective RAM,
process-visible devices, CUDA driver,
runtime, Toolkit and compiler state, optional NCCL library/version information,
other generic backends, measurement rules,
and an optional memory sample into a schema-versioned list or JSON record.

The consolidated `cuda_libraries()` table reports whether the current process
can dynamically load the CUDA driver, runtime, cuBLAS, cuFFT, cuSOLVER, cuRAND,
and NCCL. Loading is a discovery check, not an execution test: no BLAS handle,
FFT plan, solver, random generator, or NCCL communicator is created. Higher
level dependencies such as cuVS and RAFT remain consumer-package checks.

`nccl_info()` is deliberately a lightweight discovery probe. It loads the
shared library and calls `ncclGetVersion()` when available, but never creates a
communicator. Its CUDA device count describes process visibility only; it is
not evidence that collective communication works.

## Evidence shipped with this release

Real-device results are stored with the package and returned inside
`hardware_info()`. The table is intentionally narrower than the implemented
probe set.

```{r validation-evidence}
library(gpuinfo)
hardware_info()$validation
```

The real-device evidence for the version 0.2.0 candidate covers:

- Apple M3 with Metal and OpenCL on macOS;
- NVIDIA Tesla T4 with CUDA on Linux;
- NVIDIA Tesla T4 OpenCL enumeration and CUDA/OpenCL physical-device
  deduplication on Linux;
- NVIDIA RTX 5060 Ti with CUDA 13.0, compute capability 12.0, `sm_120` native
  code and `compute_120` PTX build settings on Ubuntu 22.04.5; and
- Intel OpenCL enumeration alongside the NVIDIA device on the same Linux host.

Linux ARM64 validates the CPU path and native package compilation. Hosted
Linux, Windows, Intel macOS, and Apple Silicon macOS jobs exercise package
installation, tests, and no-GPU behavior. A hosted Windows CPU runner is not
evidence of Windows NVIDIA support.

AMD ROCm and Windows NVIDIA CUDA/OpenCL remain unvalidated for this candidate.
Intel OpenCL evidence is limited to the one Linux device listed above. Other
probes are available for early testing but are not represented as validated
hardware support.

## Reproducing a real-hardware run

Use a clean public commit so the tested source is recoverable. Replace
`BACKEND` with `cuda`, `metal`, `rocm`, or `opencl`.

```{sh hardware-validation, eval=FALSE}
git clone https://github.com/tkcaccia/gpuinfo.git
cd gpuinfo
git rev-parse HEAD
R CMD build .
shasum -a 256 gpuinfo_*.tar.gz
R CMD INSTALL gpuinfo_*.tar.gz
Rscript tools/validate-hardware.R BACKEND | tee validation-output.txt
```

A successful run ends with `VALIDATION PASSED`. Preserve the full output and
record the package commit, operating system, architecture, GPU model, driver,
runtime, memory, provider, and job identifier. Cloud instances must be
terminated after collecting the evidence; merely disconnecting from or
stopping a billable GPU machine may leave charges running.

## What counts as validation

The project uses three distinct levels of testing:

1. Parser fixtures verify that representative command output is interpreted
   correctly. They do not prove hardware support.
2. CPU-only continuous integration verifies portability, installation, and
   safe behavior when accelerators are absent. It does not validate a GPU.
3. A real-hardware run must enumerate the device and expected backend and must
   complete `tools/validate-hardware.R` successfully. Only these runs are added
   to the validation evidence returned by `hardware_info()`.

This separation keeps release claims auditable and allows later package
versions to promote AMD, Intel, or Windows GPU support only after the relevant
hardware run passes.
