This guide takes you from an ordinary R matrix to PCA and nearest-neighbour results computed with CUDA. You stay in R, use familiar data structures, and do not need a deep-learning framework.
Before you start
Before opening R, make sure you have:
- Windows or Linux;
- a CUDA-capable NVIDIA GPU and working NVIDIA driver;
- a CUDA 12.x installation from NVIDIA; and
- the current cudaverse release.
Follow the GPU setup guide first if
nvidia-smi does not show a GPU or the diagnostic call below
reports missing libraries.
# install.packages("pak")
pak::pak("cudaverse/cudaverse@v0.4.1")
library(cudaverse)
check <- cuda_diagnostics()
check$summary
check$next_steps
# Stop here with a useful explanation if CUDA is not ready.
cuda_select_device("cuda")If the last command succeeds, cudaverse is ready to use the GPU. If it fails, follow the printed next step; the requested CUDA task is never silently run somewhere else.
PCA and exact nearest neighbours on CUDA
This is the most useful first workflow. A numeric R matrix enters the package, PCA reduces it to 20 components, and exact kNN finds 15 neighbours per row.
set.seed(1)
x <- matrix(rnorm(10000 * 100), nrow = 10000, ncol = 100)
pca <- cuda_pca(
x,
n_components = 20,
center = TRUE,
scale. = FALSE,
device = "cuda"
)
neighbors <- cuda_knn(
pca$x,
k = 15,
metric = "euclidean",
device = "cuda"
)
dim(pca$x)
dim(neighbors$index)
head(neighbors$index)
head(neighbors$distance)The PCA scores can stay on the GPU while kNN uses them. You can confirm where the work ran:
cuda_provenance(pca)
cuda_provenance(neighbors)
cuda_memory_info("cuda")GPU-resident matrix operations
Use a cudatensor when you want several matrix operations
to reuse the same data on the GPU. to_cpu() brings the
final result back as an ordinary R object.
x_gpu <- cuda_tensor(x, device = "cuda", dtype = "float32")
centered_gpu <- x_gpu - tensor_mean(x_gpu, dim = 1)
gram_gpu <- tensor_matmul(t(centered_gpu), centered_gpu)
column_totals_gpu <- tensor_sum(x_gpu, dim = 1)
tensor_device(gram_gpu)
gram <- to_cpu(gram_gpu)
column_totals <- to_cpu(column_totals_gpu)Subsetting, transpose, arithmetic, matrix multiplication, and summaries follow familiar R conventions while supported results remain on the GPU.
Sparse normalization, PCA, and kNN
Sparse inputs begin as Matrix objects. The same workflow
can normalize the matrix, run PCA, and find neighbours without making
the user learn another API.
counts <- Matrix::rsparsematrix(10000, 100, density = 0.03)
counts@x <- abs(counts@x)
counts_gpu <- cuda_sparse(counts, device = "cuda")
normalized_gpu <- sparse_normalize(
counts_gpu,
margin = "rows",
scale_factor = 10000,
log1p = TRUE
)
sparse_pca <- cuda_pca(
normalized_gpu,
n_components = 20,
device = "cuda"
)
sparse_neighbors <- cuda_knn(
sparse_pca$x,
k = 15,
device = "cuda"
)
sparse_info(normalized_gpu)
cuda_provenance(sparse_neighbors)Other CUDA tasks
The same strict device argument applies to decompositions, distances, and k-means:
svd_fit <- cuda_svd(x, nu = 20, nv = 20, device = "cuda")
distances <- cuda_distance(
x[1:1000, ],
x[1001:2000, ],
batch_size = 256,
device = "cuda"
)
clusters <- cuda_kmeans(
pca$x,
centers = 20,
seed = 1,
device = "cuda"
)cuda_distance() returns a complete dense matrix, so its
host output grows as nrow(x) * nrow(y). Prefer
cuda_knn() when only the nearest neighbours are needed.
Where to go next
- GPU setup and troubleshooting provides exact Windows and Linux dependency checks.
- GPU residency and provenance shows how to avoid unnecessary transfers.
- CUDA operation coverage shows which tasks run fully on the GPU and which currently include an R stage.
-
Performance explains the retained
comparisons with base R and the optional R
torchbackend.