GPU Offload in Rust: Portable, Safe, and Fast
Abstract: High-performance GPU programming has traditionally forced a compromise between execution efficiency and memory safety. While Rust guarantees compile-time memory safety for host CPUs via its strict ownership model, applying these constraints to massively parallel GPU execution environments has previously mandated either vendor-locked Domain-Specific Languages (DSLs) or escaping to explicit unsafe raw pointers. This paper presents a zero-overhead, multi-vendor GPU compilation framework built natively into the Rust compiler (rustc) and LLVM backends. We leverage Rust's rich type system, ownership system, and strict aliasing guarantees (noalias) to efficiently manage and optimize data transfers through LLVM's Offload infrastructure. We expose the technical challenges of cross-vendor ABI lowering mismatches between Host and Device targets and introduce a two-pass compilation pipeline capable of safely handling both manual and compiler-generated memory movements. Evaluating our framework on RAJAPerf demonstrates that our rustc-based solution can generate competitive LLVM IR for GPU kernels, achieving a solid kernel performance against native, hand-optimized CUDA and HIP C++ baselines.
Paper Prompts
Sign up for free to create and run prompts on this paper.
Top Community Prompts
Explain it Like I'm 14
1. What is this paper about?
This paper presents a new way to use GPUs with the Rust programming language.
GPUs are special processors that can perform many calculations at the same time. They are very useful for scientific research, artificial intelligence, weather prediction, and video games. However, programming GPUs is often difficult because programmers must carefully manage memory and avoid errors.
The authors create a system called Rust Offload. It allows Rust programs to send work from the CPU to GPUs while trying to keep three important goals:
- Portable: It should work with GPUs from different companies, especially NVIDIA and AMD.
- Safe: It should use Rust’s rules to prevent common memory errors.
- Fast: It should perform nearly as well as programs written directly in CUDA or HIP, which are common GPU technologies.
2. What questions are the researchers asking?
The paper mainly asks:
- Can Rust’s safety rules also be used when writing GPU programs?
- Can one Rust GPU system support hardware from different companies?
- Can Rust automatically move data between the CPU and GPU without making programs much slower?
- Can Rust GPU programs perform similarly to carefully optimized C++ GPU programs?
- Can programmers choose between convenience and detailed control over GPU memory?
The researchers are especially interested in avoiding a common trade-off. Usually, programmers must choose between:
- writing convenient code that may be slower, or
- writing very complicated code with unsafe pointers to get the best speed.
The goal of Rust Offload is to reduce this compromise.
3. How did the researchers build and test the system?
Using Rust’s safety rules
Rust checks many possible programming mistakes while the program is being compiled. For example, it checks:
- whether a reference points to valid data,
- whether data is used after it has been deleted,
- whether two parts of a program try to change the same data at the same time.
An analogy is a library with a strict checkout system. The library records who is borrowing each book and whether someone is allowed to write in it. This helps prevent two people from making conflicting changes.
The paper uses these rules to understand how data should move between the CPU and GPU. For example:
&Tmeans the GPU may read the data.&mut Tmeans the GPU may change the data, so the updated version may need to be copied back to the CPU.
Supporting different GPU companies
The system is built on LLVM’s Offload infrastructure. LLVM is a collection of compiler tools used by many programming languages.
Using LLVM allows the Rust compiler to produce GPU code for different targets, including:
- NVIDIA GPUs,
- AMD GPUs,
- potentially Intel and Apple GPUs in the future.
This is similar to translating one set of instructions into several languages so that people from different countries can understand it.
A two-pass compilation process
The compiler uses two main stages:
- It compiles the GPU part of the program for the chosen GPU.
- It compiles the CPU part and places the GPU code inside the final program.
This is necessary because a CPU and a GPU are different kinds of processors. They may represent data types differently or follow different rules.
The researchers also save information about the exact versions of generic functions that GPU kernels need. A generic function is a reusable function that can work with different types, somewhat like a template.
Three ways to use GPU code
Rust Offload provides three interfaces, or programming styles.
Interface A: Automatic management
The programmer writes a normal-looking Rust GPU function and uses an offload! command. The system automatically:
- copies input data to the GPU,
- runs the GPU kernel,
- copies changed data back to the CPU.
This is the easiest method, especially for beginners.
However, it can copy data too often. If several GPU tasks use the same data, the program might repeatedly move that data from the CPU to the GPU and back again.
Interface B: GPU libraries
This interface lets Rust call highly optimized GPU libraries, such as:
cuBLASfor NVIDIA GPUs,rocBLASfor AMD GPUs.
These libraries contain very fast mathematical operations. Rust Offload helps manage the data passed to them in a way similar to its own GPU kernels.
Interface C: Manual memory control
This interface gives programmers more control. They can keep data on the GPU while several kernels use it, then explicitly copy it back to the CPU when needed.
This is more complicated, but it avoids unnecessary transfers and can provide more predictable performance.
Making parallel memory access safer
Many GPU threads may run at the same time. If two threads change the same location in memory, they can interfere with each other.
The researchers introduce a structure called a Region. It divides an array into separate sections and gives each GPU thread its own section.
Imagine giving every student in a classroom a different group of questions to answer. If each student works only on their assigned questions, they will not overwrite one another’s answers.
This approach allows many common GPU programs to use ordinary Rust-style data access without requiring programmers to write unsafe code themselves.
Testing with benchmarks
The researchers translated some tests from the RAJAPerf benchmark suite into Rust. These tests imitate common high-performance computing tasks, such as:
- matrix calculations,
- vector operations,
- physical simulations,
- stencil calculations.
They tested the programs on:
- an AMD MI250X GPU,
- an NVIDIA H100 GPU,
- an NVIDIA RTX A2000 GPU.
They compared the Rust programs with C++ programs using CUDA, HIP, or RAJA.
4. What did they find?
Rust kernels were generally competitive
The Rust GPU kernels usually performed similarly to the comparison C++ kernels.
On the AMD MI250X, Rust was between 32% faster and 43% slower than the comparison implementations when measuring total benchmark runtime.
On the NVIDIA H100, Rust was between 11% faster and 46% slower than the CUDA comparison.
The biggest slowdowns happened in very small test programs. These programs perform only a few calculations, so small differences in compiler decisions—such as how much code to unroll—can have a large effect.
Automatic data movement can be extremely expensive
The easiest interface, Interface A, may copy data every time a kernel is launched. In one AMD test, this simple approach was more than 400 times slower than the version that kept data on the GPU.
This result is important because it shows that data movement can sometimes matter more than the calculation itself. Moving data between the CPU and GPU is like repeatedly carrying books between two buildings: even if reading the books is fast, the walking can take most of the time.
The explicit interface avoids this problem by keeping data on the GPU between operations.
Rust moved less data in some tests
On the NVIDIA H100, Rust transferred less data than the RAJA comparison:
| Measurement | Rust | RAJA |
|---|---|---|
| Host-to-device transfers | 53 | 55 |
| Data sent to the GPU | 423 MB | 468 MB |
| Device-to-host transfers | 9 | 9 |
| Data returned to the CPU | 69 MB | 99 MB |
Although Rust moved less data, its transfers were not always faster. The authors believe this may be related to differences in how the systems use GPU memory and asynchronous transfers.
Some floating-point optimizations improved performance
The researchers tested Rust’s experimental algebraic floating-point operations. These allow certain mathematical optimizations while avoiding some assumptions that could be unsafe.
For one small benchmark called FIR, this produced about a two-times speedup on an NVIDIA RTX A2000. Several other tests improved by about 20%.
However, these optimizations did not help every benchmark or every GPU.
Rust used slightly more registers
The Rust GPU programs used about 33 registers per thread on average, compared with 28 for one C++ CUDA version.
Registers are tiny, very fast storage spaces inside a processor. Using more registers is not automatically a serious problem, but it can reduce how many threads run at once.
The difference may partly come from Rust performing additional bounds checks to make sure array accesses are valid. The researchers did not observe a clear performance problem from these checks in their tests.
The system supports multiple GPU vendors
The prototype successfully generated GPU code for both AMD and NVIDIA hardware. This supports the paper’s goal of avoiding a system tied to only one company.
However, the system is still experimental. Some data types may be represented differently on the CPU and on different GPUs, so the compiler needs more checks before supporting more complicated values.
5. Why are these results important?
The paper shows that it may be possible to combine:
- Rust’s memory safety,
- GPU computing,
- support for different GPU brands,
- performance close to established C++ tools.
This is valuable because traditional GPU programming often depends on C or C++, where memory mistakes can cause crashes, incorrect results, or security problems.
Rust Offload could make GPU programming easier to maintain. A compiler can also use Rust’s ownership information to understand when data is read, changed, or no longer needed.
The three interfaces are useful for different situations:
- Interface A is simple and good for experimenting.
- Interface B helps programmers use existing fast GPU libraries.
- Interface C is better for large programs where performance and memory transfers must be carefully controlled.
6. Possible impact and future work
If this technology becomes mature, scientists and engineers could write one Rust program that runs on different kinds of GPUs without rewriting large parts of it. They could also receive more help from the compiler when avoiding memory errors and data races.
This could be especially useful in:
- scientific simulations,
- artificial intelligence,
- engineering,
- climate modeling,
- supercomputing,
- medical and chemical research.
The work is not finished yet. Future improvements include:
- making automatic data transfers smarter,
- adding better support for Rust’s standard library on GPUs,
- supporting more complex data types,
- checking that CPU and GPU data layouts match,
- improving performance for small kernels,
- supporting Intel and Apple GPUs as their compiler tools develop.
Overall, the paper presents a promising early system. It does not prove that Rust is always faster than CUDA or HIP, but it shows that Rust can provide a safer and more portable way to program GPUs while still achieving competitive speed.
Knowledge Gaps
Knowledge gaps, limitations, and open questions
- The claimed memory safety of GPU kernels is not formally established; the paper relies on
unsafeimplementations ofPartitioningStrategyand provides no mechanized proof, soundness theorem, or systematic verification that these implementations preserve Rust’s aliasing and race-freedom guarantees. - The safety of compiler-generated mappings from Rust mutability and reference types to OpenMP
MapToandMapToFromsemantics remains insufficiently specified, particularly for interior mutability, raw pointers, nested aggregates, and types with custom ownership behavior. - The framework does not fully define how Rust’s aliasing model corresponds to GPU execution models involving relaxed memory ordering, atomics, barriers, divergent control flow, and inter-thread synchronization.
- Shared-memory support remains explicitly
unsafe; no safe abstraction is provided for bounds, alignment, object layout, synchronization, or cooperative access among threads in a workgroup. - The supported
PartitioningStrategypatterns are not comprehensively characterized; it remains unclear which common GPU access patterns—such as reductions, stencils, indirect indexing, gathers/scatters, tiling, and irregular graphs—can be expressed without unsafe code. - The paper does not evaluate whether compiler optimizations such as bounds-check elimination can reliably remove the additional checks introduced by safe indexing, especially for dynamic problem sizes and complex indexing expressions.
- Cross-boundary ABI validation is identified as future work, leaving unresolved whether structs, tuples, enums, dynamically sized types, nested references, SIMD types, and user-defined representations can be passed safely between host and device.
- The framework lacks a complete specification for ABI incompatibilities across all supported host/device combinations, including differences in pointer width, alignment, scalar representation, calling conventions, and aggregate layout.
- The consequences of target-specific
cfgevaluation, macros, inline assembly, and architecture-specific intrinsics for generic host/device code are not systematically analyzed beyond the motivating examples. - Support for the Rust standard library on GPUs is largely absent, leaving unclear which core and allocation facilities are usable in realistic applications and how device-compatible libraries should be developed and maintained.
- The interaction between GPU offloading and Rust features such as trait objects, closures, async code, dynamically dispatched functions, panics, unwinding, thread-local storage, and custom allocators is not investigated.
- The paper does not explain how device-side allocation, deallocation, recursion, stack growth, or resource exhaustion should be represented within Rust’s ownership and error-handling models.
- The prototype supports NVIDIA and AMD targets but does not demonstrate portability across GPU generations, driver versions, operating systems, or additional vendors such as Intel and Apple.
- Intel and Apple support is presented as a future extension rather than evaluated functionality, so the practical scope of the claimed multi-vendor portability remains limited.
- The evaluation does not report compilation times, incremental-build behavior, memory consumption, binary-size overhead, or cache effectiveness for the proposed multi-pass toolchain.
- The operational complexity of the two-pass pipeline and its integration with Cargo, Bazel, Buck2, IDEs, build caching, cross-compilation, and reproducible builds is not evaluated.
- The impact of cross-pass metadata serialization and kernel monomorphization on large generic codebases is only expected to be negligible and is not empirically measured.
- The proposed automatic transfer optimizations are described as prototypes, but the paper does not provide a complete implementation assessment, correctness validation, or quantitative results showing that they consistently match explicit data movement.
- The compiler’s ability to prove that transfers are redundant is not characterized; the supported aliasing, control-flow, loop, interprocedural, and pointer-identity cases remain unspecified.
- The optimization trade-off between prefetching or preloading data and increased GPU memory consumption is not resolved, particularly for large working sets and applications with many partially overlapping kernel arguments.
- The framework does not address asynchronous execution comprehensively, including streams or queues, event dependencies, overlapping transfers with kernels, concurrent host accesses, and synchronization across multiple devices.
- Multi-GPU execution, device selection, peer-to-peer transfers, distributed memory, and NUMA-aware host allocation are outside the evaluation and remain open engineering questions.
- The runtime behavior under errors—such as failed allocations, invalid device operations, kernel faults, timeouts, and driver failures—is not described in terms of Rust’s
Result-based error handling or resource cleanup guarantees. - The evaluation uses only a subset of RAJAPerf and therefore does not establish performance or expressiveness for full applications, long-running workflows, irregular workloads, or communication-intensive HPC codes.
- The benchmarks do not include representative end-to-end applications where kernel execution, data movement, library calls, and host computation interact over extended pipelines.
- Performance comparisons are limited to selected AMD and NVIDIA systems and do not control or fully report factors such as compiler flags, driver/runtime versions, clock settings, occupancy, launch configuration, transfer modes, and warm-up methodology.
- The paper reports substantial differences in transfer time but leaves their causes—such as memory kinds, synchronization behavior, and asynchronous-transfer configuration—unmeasured and unresolved.
- The evaluation does not provide detailed breakdowns of launch overhead, synchronization overhead, host compilation overhead, transfer latency, and kernel execution time for each interface.
- The comparison with CUDA, HIP, RAJA,
rust-cuda,rust-gpu, andcuda-oxideis not a controlled study using equivalent algorithms, memory strategies, compiler settings, and optimization effort across all systems. - The claim of “zero-overhead” is not tested across the full abstraction stack; the paper does not quantify overhead from runtime mapping, reference handling, bounds checks, interface wrappers, metadata, or kernel launch preparation.
- The performance effects of safe abstractions on occupancy, register pressure, instruction count, memory coalescing, and generated synchronization are only partially examined and remain unclear for broader kernels.
- The effect of algebraic floating-point operations on numerical accuracy, reproducibility, and application-level correctness is not evaluated.
- The safety and performance implications of integrating cuBLAS, rocBLAS, and other vendor libraries through Interface B are not demonstrated with representative library workloads or complex pointer/lifetime scenarios.
- The interaction between automatic Rust-managed transfers and vendor libraries’ internal memory management, streams, asynchronous operations, and pointer modes remains unresolved.
- The framework’s diagnostics are not evaluated; users may need actionable compiler or runtime messages explaining unsupported device types, ABI mismatches, invalid partitioning strategies, and implicit synchronization.
- There is no usability study assessing whether developers can correctly choose among Interfaces A, B, and C or construct sound partitioning strategies without extensive GPU and Rust expertise.
- The long-term compatibility of the proposed compiler intrinsics and runtime interfaces with upstream Rust and LLVM evolution is not addressed, including stabilization criteria and maintenance costs.
- The paper does not define a comprehensive conformance test suite for safety, ABI compatibility, data-movement correctness, target portability, and runtime behavior.
- Security properties beyond memory safety—such as isolation between kernels, protection against malicious or faulty device code, and confidentiality of transferred host data—are not considered.
Practical Applications
Immediate Applications
- Safer GPU acceleration for HPC and scientific computing (HPC, engineering, research — deployable now with the prototype toolchain)
- Potential workflow: identify CPU hotspots, rewrite kernels using Rust slices and
Regionpartitioning strategies, compile for NVIDIA or AMD GPUs, and compare against existing CUDA/HIP implementations. - Dependencies: the modified
rustc, LLVM Offload support, suitable GPU drivers and runtimes, and manual review of kernel access patterns where custom unsafe partitioning strategies are used.
- Potential workflow: identify CPU hotspots, rewrite kernels using Rust slices and
- Portable multi-vendor GPU software deployment (cloud computing, HPC centers, software infrastructure)
- Potential products: portable Rust GPU libraries, accelerator-aware scientific applications, and build pipelines that produce executables for both H100-class NVIDIA systems and AMD MI-series systems.
- Dependencies: comparable compiler maturity and performance across vendors; the paper reports competitive but not uniformly superior performance, with some kernels slower than CUDA or HIP baselines.
- Incremental modernization of legacy scientific applications (research software, national laboratories, engineering firms)
- Potential workflow: retain existing vendor libraries, wrap them in Rust, migrate selected kernels, and use the same ownership-based argument mapping for both library calls and custom kernels.
- Dependencies: stable FFI wrappers, compatible vendor-library ABIs, and careful handling of operations whose semantics depend on floating-point reproducibility.
- Predictable GPU memory-management workflows (software engineering, real-time and performance-sensitive systems)
- Use cases: iterative solvers, multi-stage image-processing pipelines, simulation time steps, and machine-learning preprocessing pipelines where the same arrays are consumed by many successive kernels.
- Benefit: intermediate host reads are prevented while a mutable device value is active, reducing accidental synchronization and repeated transfers.
- Dependencies: developers must understand GPU residency and explicitly choose synchronization points; the paper shows that naive automatic transfers can be more than 400 times slower than explicit data management in transfer-sensitive workloads.
- Safe parallel array and tensor kernels (scientific computing, data analytics, robotics, graphics)
- Potential tools: reusable partitioning-strategy crates for one-dimensional, multidimensional, tiled, and chunked data layouts.
- Dependencies: each partitioning strategy must correctly uphold its unsafe contract. Irregular indexing, atomics, synchronization-heavy algorithms, and overlapping writes may still require unsafe code or additional abstractions.
- GPU acceleration of domain-specific Rust applications (healthcare, energy, chemistry, finance, and industrial analytics)
- Dependencies: the paper evaluates representative kernels rather than complete end-to-end applications. Real-world benefits depend on data-transfer volume, kernel granularity, numerical requirements, and availability of GPU-compatible dependencies.
- Unified profiling and diagnostics for offloaded operations (developer tooling and production operations)
- Potential products: Cargo-integrated profilers, transfer-overhead reports, race-safety diagnostics, and CI benchmarks comparing CPU, AMD, and NVIDIA execution.
- Dependencies: runtime instrumentation and profiling integrations still need to be developed; opaque vendor-library internals limit visibility into the library’s internal kernels.
- Safer educational materials for heterogeneous programming (academia and workforce training)
- Dependencies: the experimental compiler workflow must become easier to install and document, and students still need exposure to GPU-specific concepts such as occupancy, synchronization, shared memory, and memory coalescing.
- Rust-based build and deployment automation for accelerators (DevOps, research computing, cloud platforms)
- Dependencies: the current
-Z offloadworkflow is experimental, and production use requires stable compiler interfaces, cache-friendly packaging, robust cross-compilation, and versioned GPU runtime dependencies.
- Dependencies: the current
Long-Term Applications
- Production-grade portable GPU application frameworks (HPC, cloud infrastructure, enterprise software)
- Dependencies: upstream LLVM and Rust support for additional targets, stable APIs, broad standard-library support, mature debugging tools, and performance parity across vendors.
- Automatic optimization of host/device data movement (compiler technology and high-performance software)
- Potential tools: compiler passes that hoist transfers out of loops, eliminate redundant transfers, overlap communication with computation, and select whether preloading is worthwhile based on memory pressure.
- Dependencies: accurate alias, lifetime, and host-use analysis; reliable cost models; stream/event support in the runtime; and safeguards against increasing GPU memory consumption excessively.
- Safe abstractions for shared memory and cooperative GPU algorithms (robotics, simulation, machine learning, image processing)
- Dependencies: shared memory is inherently concurrently accessed, so standard Rust references cannot be used directly without specialized synchronization-aware types. Some low-level unsafe implementation remains likely.
- Verified or formally checked GPU memory partitioning (safety-critical engineering, aerospace, healthcare, autonomous systems)
- Dependencies: formal models must account for GPU execution semantics, synchronization, atomics, compiler transformations, and vendor-specific behavior. The current framework does not itself formally verify custom unsafe strategies.
- Accelerated privacy-sensitive and safety-sensitive applications (healthcare, finance, government)
- Dependencies: memory safety does not automatically provide confidentiality, side-channel resistance, numerical correctness, or regulatory compliance. Applications would require secure GPU runtimes, auditable dependencies, encryption policies, and domain-specific validation.
- Rust-native machine-learning and tensor ecosystems (AI, scientific machine learning, edge computing)
- Dependencies: support for richer tensor types, asynchronous execution, GPU-compatible standard-library functionality, automatic differentiation, kernel fusion, and integration with established model formats and vendor libraries.
- GPU acceleration for robotics and autonomous systems (robotics, automotive, drones, industrial automation)
- Dependencies: deterministic scheduling, bounded latency, real-time guarantees, support for heterogeneous sensors and devices, and validation of GPU driver behavior. The paper demonstrates performance portability but does not establish real-time guarantees.
- Energy-efficient edge and embedded acceleration (IoT, mobile devices, industrial monitoring)
- Dependencies: the paper’s demonstrated targets are NVIDIA and AMD GPUs; mobile and embedded GPUs often have different memory models, toolchains, power constraints, and API limitations.
- Standardized cross-language accelerator interoperability (academia, language design, compiler infrastructure)
- Dependencies: ABI compatibility across host and device targets is not currently guaranteed. The paper specifically identifies differing slice layouts between x86-64, AMD GPU, and NVIDIA GPU targets, so automated boundary validation is essential.
- Improved numerical-performance controls for scientific workloads (climate modeling, physics, chemistry, finance)
- Dependencies: relaxed arithmetic may affect reproducibility, stability, NaN/infinity handling, and regulatory requirements. Such controls should be explicit, documented, and accompanied by numerical validation rather than applied globally.
- A broader GPU-capable Rust standard library (general systems software and application development)
- Dependencies: GPU execution environments lack many CPU facilities, including unrestricted allocation, operating-system services, and conventional I/O. A GPU standard library would therefore require carefully defined subsets and target-specific semantics.
Glossary
- ABI (Application Binary Interface): A specification governing how compiled components represent data and call one another at the binary level. “type layouts and ABI lowerings can diverge between the two targets.”
- Aliasing: The condition in which multiple references or pointers access the same memory location. “Rust’s strict ownership and aliasing guarantees provide benefits that extend beyond basic software correctness”
- Aliasing XOR mutability rule: Rust’s rule that memory may have either one mutable reference or multiple immutable references, but not both simultaneously. “This rule is often referred to as the aliasing XOR mutability rule.”
- AST (Abstract Syntax Tree): A tree representation of source-code structure used by compilers during parsing and analysis. “the guarded function, module, or struct will be discarded at the Abstract Syntax Tree (AST) level”
- Backend: The compiler component that converts an intermediate representation into target-specific machine code. “LLVM backends”
- Bitcode: A serialized, low-level intermediate representation used by LLVM for later compilation or linking. “instead of just emitting device bitcode, our compiler wraps the bitcode into a device binary.”
- Borrow checker: The Rust compiler mechanism that statically verifies ownership, borrowing, lifetime, and aliasing rules. “With PreloadMut, such a read is rejected by the borrow checker until the mutably preloaded value is dropped”
- Code generation (codegen): The compiler phase that produces executable or intermediate machine-oriented code from analyzed program representations. “the first host pass can skip the expensive codegen part.”
- Compile-time monomorphization: The process of generating specialized function or data-structure implementations for concrete generic types. “Idiomatic Rust relies on compile-time monomorphization to generate specialized, concrete function instances from generic definitions.”
- Cross-compilation: Compiling software on one platform for execution on a different target platform or architecture. “Code can either be compiled for the host on which the compiler is running or cross-compiled for a different target.”
- Data race: An unsafe concurrent access pattern in which threads access shared memory without appropriate synchronization, with at least one access being a write. “Rust [has] the ability to enforce[22] memory safety and data-race freedom at compile time.”
- Data mapper: A runtime or compiler mechanism that associates host data with corresponding device allocations and manages their transfers. “The LLVM backend of rustc lowers the constructor of the Preload values into an offload begin-data-mapper operation”
- Data prefetching: Moving data toward a processing device before it is needed to reduce waiting time. “Automatic data prefetching.”
- Device binary: Compiled executable code intended for a GPU or other accelerator rather than the host CPU. “our compiler wraps the bitcode into a device binary.”
- Domain-Specific Language (DSL): A programming language designed for a particular application domain rather than general-purpose programming. “vendor-locked Domain-Specific Languages (DSLs)”
- Fat binary: An executable containing code or binaries for multiple architectures or hardware targets. “the underlying LLVM Offload infrastructure supports fat binaries”
- FFI (Foreign-Function Interface): A mechanism for calling functions or using libraries implemented in another programming language. “Their usage is very uncommon outside of Foreign-Function-Interfaces (FFI).”
- Frontend: The compiler component that parses source code, performs language-specific analysis, and produces an intermediate representation. “We build our work on top of LLVM’s OpenMP/Offload library.”
- Heterogeneous execution: Computation distributed across different processor types, such as CPUs and GPUs. “We define three programming models, from a fully automated host-managed execution to explicit, type-enforced device memory layout control.”
- HPC (High-Performance Computing): The use of powerful parallel computing systems to solve computationally intensive problems. “High-performance computing (HPC) and scientific applications remain heavily dominated by memory-unsafe languages”
- Implicit synchronization: Synchronization performed automatically by a runtime or compiler rather than explicitly requested by the programmer. “chains of kernels can suffer from implicit host-device synchronization”
- Intermediate Representation (IR): A compiler-internal representation of a program used between source-language processing and machine-code generation. “Our design introduces a two-pass compilation pipeline that strictly separates host and device intermediate representations (IR).”
- Interior mutability: A Rust pattern allowing mutation through an otherwise immutable reference, subject to special safety rules. “An advanced concept which allows users to create a const reference to a mutable object wrapped in an UnsafeCell.”
- LICM (Loop-Invariant Code Motion): An optimization that moves calculations or operations unchanged by a loop outside that loop. “The second optimization we prototyped is a variant of Loop-Invariant-Code-Motion (LICM).”
- LLVM IR: LLVM’s compiler intermediate representation, designed for optimization and translation to multiple machine targets. “The host invocation is lowered into runtime calls that allocate device storage”
- MapTo directive: An offload mapping operation that copies data from host memory to device memory. “Immutable references (&T) and constant raw pointers (*const T) lower to the MapTo directive”
- MapToFrom directive: An offload mapping operation that transfers data to the device and synchronizes modified data back to the host. “Mutable references (&mut T) and mutable raw pointers (*mut T) lower to the bidirectional MapToFromdirective”
- MIR (Mid-level Intermediate Representation): A Rust compiler representation used for analysis and transformations after high-level language processing. “A Mid-level Intermediate Representation (MIR) analysis enabling the automatic and efficient generation of memory transfers”
- Memory safety: The property that programs do not perform invalid memory accesses or related forms of memory corruption. “Rust guarantees compile-time memory safety for host CPUs via its strict ownership model”
- Microarchitectural constraint: A performance or implementation limitation arising from the internal organization of a processor. “software-managed runtimes often introduce notable performance overhead and microarchitectural constraints”
- Noalias metadata: Compiler information asserting that a pointer does not alias another pointer during a relevant computation. “The only exception is described next.”
- Offload: The execution of computation or management of data on an accelerator instead of the host processor. “To close these gaps, we present a cross-vendor interface for GPU programming in Rust”
- OpenMP target runtime: The runtime system supporting OpenMP operations directed at accelerator devices. “Rustc currently generates OpenMP target runtime calls”
- Ownership model: Rust’s compile-time system for determining which part of a program controls a value and when that value may be accessed or destroyed. “While Rust guarantees compile-time memory safety for host CPUs via its strict ownership model”
- Partitioning strategy: A rule or implementation that assigns distinct portions of data to parallel threads to prevent conflicting accesses. “Every Region is tied to a PartitioningStrategy.”
- Raw pointer: A pointer without Rust references’ automatic validity, lifetime, and aliasing guarantees. “Rust offers raw, c-style pointers, e.g. ∗mut f32.”
- RAJAPerf: A benchmark suite for measuring performance of loop-level parallel kernels across accelerator backends. “RAJAPerf is the benchmark suite of RAJA”
- Scratchpad memory: Fast, temporary memory used by threads or thread groups during computation. “Advanced GPU algorithms frequently utilize shared memory as a high-performance scratchpad”
- SPIR-V: A portable intermediate binary representation used by Vulkan and other graphics or compute APIs. “rust-gpu, based on SPIR-V”
- Target triple: A compiler identifier specifying a target architecture, operating system, and environment. “This device pass uses existing upstream target definitions for AMD and NVIDIA architectures”
- Two-pass compilation: A compilation design in which host and device code are compiled in separate compiler passes. “For our toolchain, we implement a two-pass compilation pipeline”
- Undefined Behaviour (UB): Program behavior for which the language specification imposes no requirements, often resulting from invalid memory operations or data races. “The trait here is unsafe, because any incorrect implementation of a PartitionStrategy will likely result in Undefined Behaviour.”
- Unified Shared Memory (USM): A programming abstraction presenting memory shared between host and accelerator address spaces. “Discrete accelerator memory spaces typically require explicit, developer-directed data mapping, as alternative Unified Shared Memory (USM) abstractions”
- UnsafeCell: A Rust primitive that permits controlled mutation through shared references and serves as the basis for interior mutability. “a mutable object wrapped in an UnsafeCell.”
- Vendor-agnostic: Designed to operate across hardware or software providers rather than being tied to one vendor. “vendor-agnostic vs. vendor specific”
- Vectorization: An optimization that executes the same operation on multiple data elements simultaneously using vector instructions. “Algebraic floats allow LLVM to further vectorize it with a vector-width of 4”
- Workgroup shared memory: Fast GPU memory allocated for and shared among threads in a single thread block or workgroup. “This memory space is private to each thread block, allowing threads within the same block to efficiently exchange data.”