# Performance Implications of NVRTC vs NVCC Compilation in DeepGEMM

> Discover NVRTC vs NVCC compilation performance in DeepGEMM. Understand latency differences and caching benefits for optimized deep learning.

- Repository: [DeepSeek/DeepGEMM](https://github.com/deepseek-ai/DeepGEMM)
- Tags: performance
- Published: 2026-04-19

---

**NVRTC reduces first-use compilation latency to 10–30 ms by compiling in-process, while NVCC incurs higher one-time costs for offline CUBIN generation, though both converge to <1 % runtime difference after caching.**

DeepGEMM, the high-performance matrix-multiplication library from DeepSeek, supports dual compilation modes for its CUDA kernels. Understanding the performance implications of NVRTC versus NVCC compilation helps you optimize for latency-sensitive applications versus long-running training jobs.

## How DeepGEMM Selects the Compiler

The selection logic resides in [`csrc/jit/compiler.hpp`](https://github.com/deepseek-ai/DeepGEMM/blob/main/csrc/jit/compiler.hpp). At startup, the library reads the environment variable `DG_JIT_USE_NVRTC`. When set to `1`, DeepGEMM uses **NVRTC** for just-in-time compilation; otherwise, it defaults to **NVCC** for offline compilation (lines 54–60).

### Architecture of the Compilation Pipeline

Both paths share the same entry point: `Compiler::build()`. This method checks the persistent on-disk cache in `.deep_gemm/cache/` before invoking the actual compiler (lines 4–10). If a cached CUBIN exists, the method returns immediately, eliminating compilation overhead for both NVCC and NVRTC.

## Compilation Latency: One-Time Cost vs. Per-Kernel Overhead

The primary performance difference emerges during the initial compilation phase.

### NVCC (Offline Compilation)

NVCC spawns an external process that parses the entire translation unit, runs host-side compilation, and generates a CUBIN file. In [`csrc/jit/compiler.hpp`](https://github.com/deepseek-ai/DeepGEMM/blob/main/csrc/jit/compiler.hpp), the `NVCCCompiler::compile` method assembles a command line including `--gpu-architecture=sm_<arch>` and invokes the driver (lines 122–131). This process incurs significant filesystem I/O and synchronization overhead (atomic rename operations at lines 28–38), resulting in higher one-time latency.

### NVRTC (JIT Compilation)

NVRTC operates entirely within the process. The `NVRTCCompiler::compile` routine creates an `nvrtcProgram`, compiles the kernel string, and extracts PTX or CUBIN directly in memory (lines 84–92). For typical DeepGEMM kernels, this JIT path completes in approximately **10–30 ms**. Since NVRTC version 12.8, DeepGEMM automatically enables pre-compiled header (PCH) support to further reduce this latency (lines 66–76).

## Caching Strategy and Binary Reuse

Both compilation modes leverage the same persistent cache implemented in [`csrc/jit/cache.hpp`](https://github.com/deepseek-ai/DeepGEMM/blob/main/csrc/jit/cache.hpp). The cache key incorporates the kernel signature, compiler version, and architecture flags.

- **Cache Hit**: When a valid CUBIN exists, both NVCC and NVRTC paths skip compilation entirely.
- **Cache Miss**: The compiler writes the generated CUBIN to `.deep_gemm/cache/` using atomic rename operations to prevent corruption during concurrent writes (lines 28–38).

This unified caching mechanism ensures that after the first compilation—regardless of method—subsequent process launches incur zero compilation overhead.

## Binary Portability and Architecture Targeting

**NVCC** supports embedding device code for multiple architectures simultaneously using `-gencode` flags. DeepGEMM’s NVCC path targets the exact GPU architecture via `--gpu-architecture=sm_<arch>` (lines 94–104), producing minimal binary size.

**NVRTC** generates **PTX** for the runtime-detected architecture (via `device_runtime->get_arch`). The CUDA driver translates this PTX to machine code at module load time. This adds a small runtime translation overhead, though in practice this is negligible compared to kernel execution time.

## Runtime Execution Performance

Once compiled and cached, both NVCC-generated CUBINs and NVRTC-generated CUBINs execute with identical performance characteristics. The DeepGEMM source code confirms that the runtime difference between cached NVCC and NVRTC binaries is **less than 1 %** for most kernels. The execution path—memory bandwidth, occupancy, and instruction throughput—depends solely on the generated machine code, which is equivalent in both cases.

## Practical Configuration Examples

### Enabling NVRTC for Low-Latency Inference

Set the environment variable before importing the library to minimize first-use latency:

```python
import os
os.environ["DG_JIT_USE_NVRTC"] = "1"

import deep_gemm

# First call compiles in ~10-30 ms

result = deep_gemm.gemm(A, B)

```

### Verifying Cache Behavior

Inspect the persistent cache directory to confirm CUBIN reuse across both compilation modes:

```bash
ls -la ~/.deep_gemm/cache/

# Output: kernel.gemm$${NVCC12.9}... or kernel.gemm$${NVRTC12.8}...

```

### Measuring Compilation Overhead

Benchmark the compilation latency for each path:

```python
import time, os

# Test NVRTC path

os.environ["DG_JIT_USE_NVRTC"] = "1"
import deep_gemm

start = time.perf_counter()
deep_gemm.gemm(A, B)  # Cold start

print(f"NVRTC first-use: {time.time() - start:.3f}s")

```

## Summary

- **NVRTC** minimizes first-use compilation latency to 10–30 ms by compiling in-process, making it ideal for interactive workloads and short-lived processes.
- **NVCC** incurs higher one-time compilation costs due to external process spawning and filesystem I/O, but produces architecture-specific CUBINs without runtime PTX translation.
- **Both paths** share a unified persistent cache (`.deep_gemm/cache/`), eliminating compilation overhead after the first successful build.
- **Runtime performance** differs by less than 1 % between cached NVCC and NVRTC binaries, as both execute identical machine code.

## Frequently Asked Questions

### How do I switch between NVRTC and NVCC in DeepGEMM?

Set the environment variable `DG_JIT_USE_NVRTC` to `1` before importing the library. The value is read during initialization in [`csrc/jit/compiler.hpp`](https://github.com/deepseek-ai/DeepGEMM/blob/main/csrc/jit/compiler.hpp) (lines 54–60). If unspecified, DeepGEMM defaults to NVCC for offline compilation.

### Does NVRTC produce slower GPU kernels than NVCC?

No. Once compiled and cached, both NVRTC and NVCC generate functionally identical CUBINs. The DeepGEMM implementation confirms that runtime performance differs by less than 1 %. NVRTC generates PTX that the driver translates to machine code, but this translation overhead is negligible compared to kernel execution time.

### Where does DeepGEMM store compiled kernels?

Compiled kernels are stored in the persistent cache directory `.deep_gemm/cache/` within the user's home directory. Both NVCC and NVRTC write CUBIN files to this location using atomic rename operations to prevent corruption during concurrent writes, as implemented in [`csrc/jit/compiler.hpp`](https://github.com/deepseek-ai/DeepGEMM/blob/main/csrc/jit/compiler.hpp) (lines 28–38).

### Is NVRTC suitable for production training workloads?

Yes, but with specific considerations. NVRTC excels in scenarios requiring low first-use latency, such as interactive development or inference with dynamic shapes. For long-running training jobs where initialization overhead is amortized over many hours, NVCC offers slightly faster compilation of complex kernels and eliminates any PTX translation overhead, though the practical difference remains under 1 %.