#![allow(clippy::wildcard_imports)]
use std::collections::HashMap;
use std::sync::{Mutex, OnceLock};
static CUDA_SENTINEL: Mutex<Option<CudaContext>> = Mutex::new(None);
static STREAM_POOL: Mutex<Option<(CudaStream, CudaStream, CudaStream)>> = Mutex::new(None);
static CONTEXT_POOL: Mutex<Option<CudaContext>> = Mutex::new(None);
fn lock_pool<'a, T>(
pool: &'a Mutex<T>,
name: &str,
operation: &str,
) -> std::sync::MutexGuard<'a, T> {
pool.lock()
.unwrap_or_else(|_| panic!("{name} mutex poisoned during {operation}"))
}
fn ensure_sentinel(device_ordinal: i32) -> Result<(), GpuError> {
let count = device_count()?;
if device_ordinal < 0 || device_ordinal as usize >= count {
return Err(GpuError::DeviceNotFound(device_ordinal, count));
}
let mut guard = lock_pool(&CUDA_SENTINEL, "CUDA_SENTINEL", "ensure_sentinel");
if let Some(ref ctx) = *guard {
if ctx.make_current().is_ok() && ctx.synchronize().is_ok() {
return Ok(());
}
*lock_pool(&STREAM_POOL, "STREAM_POOL", "context reset") = None;
*lock_pool(&CONTEXT_POOL, "CONTEXT_POOL", "context reset") = None;
*guard = None;
eprintln!(
"[CUDA-FAILFAST] Primary context poisoned — destroyed and recreating. \
This means a kernel crashed in a previous test."
);
}
*guard = Some(CudaContext::new(device_ordinal)?);
Ok(())
}
fn checkout_context(device_ordinal: i32) -> Result<CudaContext, GpuError> {
let mut guard = lock_pool(&CONTEXT_POOL, "CONTEXT_POOL", "checkout");
if let Some(ctx) = guard.take() {
ctx.make_current()?;
ctx.synchronize().map_err(|e| {
eprintln!("[CUDA-FAILFAST] Pooled context is poisoned: {e:?}");
e
})?;
return Ok(ctx);
}
drop(guard);
CudaContext::new(device_ordinal)
}
fn checkin_context(ctx: CudaContext) {
let mut guard = lock_pool(&CONTEXT_POOL, "CONTEXT_POOL", "checkin");
if guard.is_none() {
*guard = Some(ctx);
}
}
fn checkout_streams(ctx: &CudaContext) -> Result<(CudaStream, CudaStream, CudaStream), GpuError> {
let mut guard = lock_pool(&STREAM_POOL, "STREAM_POOL", "checkout");
if let Some(streams) = guard.take() {
return Ok(streams);
}
drop(guard);
if verbose() {
eprintln!("[CUDA-POOL] Stream pool empty, creating fresh streams");
}
let s1 = CudaStream::new(ctx)?;
let s2 = CudaStream::new(ctx)?;
let s3 = CudaStream::new(ctx)?;
Ok((s1, s2, s3))
}
fn checkin_streams(s1: CudaStream, s2: CudaStream, s3: CudaStream) {
*lock_pool(&STREAM_POOL, "STREAM_POOL", "checkin") = Some((s1, s2, s3));
}
pub(crate) struct PoolableStream(Option<CudaStream>);
impl PoolableStream {
fn new(stream: CudaStream) -> Self {
Self(Some(stream))
}
fn take(&mut self) -> Option<CudaStream> {
self.0.take()
}
}
impl std::ops::Deref for PoolableStream {
type Target = CudaStream;
fn deref(&self) -> &CudaStream {
self.0.as_ref().expect("stream was already taken")
}
}
impl std::ops::DerefMut for PoolableStream {
fn deref_mut(&mut self) -> &mut CudaStream {
self.0.as_mut().expect("stream was already taken")
}
}
impl Drop for PoolableStream {
fn drop(&mut self) {
}
}
use trueno_gpu::driver::sys::CUfunction;
use trueno_gpu::driver::{
cuda_available, device_count, CaptureMode, CudaContext, CudaEvent, CudaGraph, CudaGraphExec,
CudaModule, CudaStream, GpuBuffer, LaunchConfig,
};
#[derive(Clone)]
pub(crate) struct RecordedKernel {
pub func: SendCUfunction,
pub config: LaunchConfig,
pub arg_data: Vec<u64>,
}
#[derive(Clone, Copy)]
pub(crate) struct SendCUfunction(pub CUfunction);
unsafe impl Send for SendCUfunction {}
unsafe impl Sync for SendCUfunction {}
#[allow(unused_imports)]
use trueno_gpu::kernels::{
Activation, ArgMaxFinalKernel, ArgMaxKernel, AttentionKernel,
BatchedIncrementalAttentionKernel, BatchedQ4KGemvKernel, BatchedQ6KGemvKernel,
BatchedResidualAddKernel, BatchedRopeKernel, BatchedSwigluKernel,
BatchedVectorizedRmsNormKernel, BiasActivationKernel, ChunkedTiledQ4KGemvKernel,
CoalescedGemvKernel, CoalescedQ4KGemvKernel, CoalescedQ6KGemvKernel, Dp4aQ4KGemvKernel,
ElementwiseMulKernel, Fp16Q4KGemvKernel, FusedGateUpKernel, FusedGateUpQ4KGemvKernel,
FusedGateUpSwigluHwDp4aQ4KGemvKernel, FusedQKVKernel, FusedResidualRmsNormKernel,
FusedRmsNormQ4KGemvKernel, FusedSwigluKernel, GeluKernel, GemmKernel, GemvKernel,
IncrementalAttentionKernel, Kernel, KvCacheScatterIndirectKernel, KvCacheScatterKernel,
LayerNormKernel, MultiWarpIncrementalAttentionKernel, PackedDp4aQ4KQ8Kernel,
PerHeadRmsNormKernel, PreciseRmsNormKernel, PreciseRopeIndirectKernel, Q4KGemvKernel,
Q4KQ8DotKernel, Q4_0GemvKernel, Q4_1GemvKernel, Q5KGemvKernel, Q5KKernel, Q5_0GemvKernel,
Q6KGemvKernel, Q6KKernel, Q8QuantizeKernel, Q8_0GemvKernel, QuantizeKernel, ResidualAddKernel,
RmsNormKernel, RopeIndirectKernel, RopeKernel, RopeNeoxIndirectKernel, RopeNeoxKernel,
SiluKernel, SoftmaxKernel, TensorCoreQ4KGemmKernel, TiledQ4KGemvKernel, TrueDp4aQ4KGemvKernel,
VectorizedQ4KGemvKernel, VectorizedRmsNormKernel,
};
use trueno_gpu::GpuError;
use crate::cuda::gpu_profile::{GpuProfile, Q4kVariant};
use crate::cuda::kernels::{CudaKernels, KernelType};
use crate::cuda::memory::{
GpuMemoryPool, PinnedHostBuffer, PoolStats, StagingBufferPool, StagingPoolStats,
};
use crate::cuda::types::{
IndexedLayerWeights, TransformerWorkspace, ValidatedLayerWeights, WeightQuantType,
};
#[inline]
fn validate_device_ptr(ptr: u64, name: &str) -> Result<(), GpuError> {
if ptr == 0 {
return Err(GpuError::InvalidParameter(format!(
"{name}: null device pointer (0x0) — refusing to launch kernel \
to prevent unrecoverable GPU device poisoning"
)));
}
Ok(())
}
mod activations;
mod attention;
mod bound_dispatch;
mod core;
mod gemm;
mod graph_builder;
mod graph_dispatch;
mod kv_cache;
mod layer;
mod layers;
mod q4k;
mod q_basic;
mod quantized;
mod weights;
mod workspace;
#[cfg(test)]
mod tests;
#[cfg(test)]
mod proptests;
#[cfg(test)]
mod gqa_parity_tests;
#[cfg(test)]
mod test_fixtures;
#[cfg(test)]
mod poison_trace_test;
#[cfg(test)]
#[path = "tests_cov003_preload.rs"]
mod tests_cov003_preload;
#[cfg(test)]
#[path = "tests_03.rs"]
mod tests_03;
#[cfg(test)]
#[path = "tests_04.rs"]
mod tests_04;
#[cfg(test)]
#[path = "tests_cov021_q4k.rs"]
mod tests_cov021_q4k;
#[cfg(test)]
#[path = "tests_zeroed_layer.rs"]
mod tests_zeroed_layer;
static BROKEN_PTX: std::sync::LazyLock<Mutex<std::collections::HashSet<u64>>> =
std::sync::LazyLock::new(|| Mutex::new(std::collections::HashSet::new()));
fn verbose() -> bool {
static VERBOSE: OnceLock<bool> = OnceLock::new();
*VERBOSE.get_or_init(|| std::env::var("REALIZAR_VERBOSE").is_ok())
}
pub struct CudaExecutor {
kernels: CudaKernels,
memory_pool: GpuMemoryPool,
staging_pool: StagingBufferPool,
modules: std::mem::ManuallyDrop<HashMap<String, CudaModule>>,
weight_cache: HashMap<String, GpuBuffer<f32>>,
named_fp16_weight_cache: HashMap<String, GpuBuffer<u16>>,
quantized_weight_cache: HashMap<String, GpuBuffer<u8>>,
quantized_weight_types: HashMap<String, u32>,
quantized_weight_pool: Option<GpuBuffer<u8>>,
quantized_weight_pool_entries: HashMap<String, (u64, usize)>,
rmsnorm_cache: HashMap<String, GpuBuffer<f32>>,
bias_cache: HashMap<String, GpuBuffer<f32>>,
indexed_layer_weights: Vec<ValidatedLayerWeights>,
output_norm_ptr: u64,
output_norm_len: usize,
lm_head_ptr: u64,
lm_head_len: usize,
lm_head_qtype: WeightQuantType,
lm_head_bias_ptr: u64,
lm_head_bias_len: usize,
logits_buffer: Option<GpuBuffer<f32>>,
logits_buffer_size: usize,
workspace: TransformerWorkspace,
gemv_input_buffer: Option<GpuBuffer<f32>>,
gemv_output_buffer: Option<GpuBuffer<f32>>,
gemv_input_size: usize, gemv_output_size: usize, gemv_output_buffer_b: Option<GpuBuffer<f32>>,
gemv_output_buffer_c: Option<GpuBuffer<f32>>,
gemv_output_size_b: usize,
gemv_output_size_c: usize,
kv_cache_gpu: HashMap<String, GpuBuffer<f32>>,
kv_cache_lengths: HashMap<usize, usize>,
kv_cache_max_len: usize,
kv_num_heads: usize, kv_num_kv_heads: usize, kv_head_dim: usize,
rope_theta: f32,
rope_type: u32,
compute_stream: PoolableStream,
transfer_stream: PoolableStream,
stream: PoolableStream,
decode_graph: Option<CudaGraphExec>,
decode_event: Option<CudaEvent>,
attention_event: Option<CudaEvent>,
position_buf: Option<GpuBuffer<u32>>,
seq_len_buf: Option<GpuBuffer<u32>>,
batched_kv_k_caches: HashMap<usize, GpuBuffer<f32>>,
batched_kv_v_caches: HashMap<usize, GpuBuffer<f32>>,
batched_kv_lengths: Vec<usize>,
batched_k_ptrs: Option<GpuBuffer<u64>>,
batched_v_ptrs: Option<GpuBuffer<u64>>,
batched_seq_lens_gpu: Option<GpuBuffer<u32>>,
batched_k_ptrs_per_layer: HashMap<usize, GpuBuffer<u64>>,
batched_v_ptrs_per_layer: HashMap<usize, GpuBuffer<u64>>,
pub(crate) batched_kv_stride: usize,
batched_kv_allocated_batch: usize,
batched_decode_graphs: HashMap<usize, CudaGraphExec>,
batched_graph_input_buf: Option<GpuBuffer<f32>>,
batched_graph_positions_buf: Option<GpuBuffer<u32>>,
batched_graph_seq_lens_buf: Option<GpuBuffer<u32>>,
batched_graph_batch_size: usize,
prefill_graphs: HashMap<usize, CudaGraphExec>,
prefill_graph_input_buf: Option<GpuBuffer<f32>>,
graph_input_buf: Option<GpuBuffer<f32>>,
decode_token_count: usize,
argmax_block_vals: Option<GpuBuffer<f32>>,
argmax_block_idxs: Option<GpuBuffer<u32>>,
argmax_result: Option<GpuBuffer<u32>>,
argmax_num_blocks: u32,
batched_argmax_results: Option<GpuBuffer<u32>>,
batched_argmax_results_cap: usize,
batched_decode_input_buf: Option<GpuBuffer<f32>>,
batched_decode_input_cap: usize,
graph_capture_failed: bool,
prefill_graph_capture_failed: bool,
is_capturing: bool,
graph_recording: bool,
graph_recorded_kernels: Vec<RecordedKernel>,
is_prefilling: bool,
flash_decode_partials: Option<GpuBuffer<f32>>,
flash_decode_max_seq_len: usize,
flash_decode_enabled: bool,
flash_decode_k_ptrs: HashMap<usize, GpuBuffer<u64>>,
flash_decode_v_ptrs: HashMap<usize, GpuBuffer<u64>>,
flash_decode_max_chunks: usize,
flash_decode_seq_lens_buf: Option<GpuBuffer<u32>>,
kv_cache_q8_enabled: bool,
kv_cache_q8_k: HashMap<String, GpuBuffer<i8>>,
kv_cache_q8_v: HashMap<String, GpuBuffer<i8>>,
kv_cache_q8_k_scales: HashMap<String, GpuBuffer<f32>>,
kv_cache_q8_v_scales: HashMap<String, GpuBuffer<f32>>,
cublas_handle: Option<trueno_gpu::driver::CublasHandle>,
cublas_workspace: Option<GpuBuffer<u8>>,
dequant_scratch: Option<GpuBuffer<f32>>,
dequant_scratch_size: usize,
fp16_weight_cache: HashMap<u64, GpuBuffer<u16>>,
fp16_activation_scratch: Option<GpuBuffer<u16>>,
fp16_activation_scratch_size: usize,
fp16_dequant_temp: Option<GpuBuffer<u16>>,
cublaslt_handle: Option<trueno_gpu::driver::CublasLtHandle>,
fp8_weight_cache: HashMap<u64, GpuBuffer<u8>>,
fp8_activation_scratch: Option<GpuBuffer<u8>>,
fp8_activation_scratch_size: usize,
fp8_weight_scales: HashMap<u64, f32>,
fp8_act_scale_buf: Option<GpuBuffer<f32>>,
fp8_absmax_buf: Option<GpuBuffer<u32>>,
fp8_act_dequant_buf: Option<GpuBuffer<f32>>,
interleaved_weight_cache: HashMap<u64, GpuBuffer<u8>>,
wmma_scratch: Option<GpuBuffer<f32>>,
dp4a_q8_scratch: Option<GpuBuffer<u8>>,
prefill_attn_scores: Option<GpuBuffer<f32>>,
prefill_attn_scores_size: usize,
optimal_tile_size: u32,
profiler: trueno::BrickProfiler,
pub(crate) gpu_profile: GpuProfile,
num_sms: u32,
q8_activation_valid: bool,
fp8_activation_cache_key: Option<(u64, u32)>,
pub(crate) graph_dispatch_positions: Vec<u32>,
pub(crate) batched_done_mask: Vec<bool>,
pub(crate) hgemm_batched_decode_active: bool,
context: std::mem::ManuallyDrop<CudaContext>,
#[cfg(all(test, feature = "cuda"))]
_gpu_test_permit: crate::test_gpu_cap::GpuTestPermit,
}