Skip to content

FastLM/TinyServe

Repository files navigation

TinyServe: Query-Aware Cache Selection for Efficient LLM Serving

A comprehensive implementation of cutting-edge LLM serving optimizations:

  1. vLLM Optimized Page Allocation Kernel - Advanced memory management for large language model inference
  2. Query-Aware Cache Selection - Intelligent cache management for efficient LLM inference
  3. TinyServe Optimized Kernels - Enhanced CUDA kernels with FlashAttention integration

πŸš€ vLLM Page Allocation Kernel

Overview

vLLM's PagedAttention is a revolutionary memory management technique inspired by operating system virtual memory paging. It dramatically improves GPU memory utilization and inference throughput for large language models.

Key Features

  • Memory Efficiency: Reduces memory waste to less than 4% (compared to 60-80% in traditional methods)
  • Performance Boost: Up to 30x improvement in inference throughput
  • Memory Sharing: Enables sharing of physical blocks between different sequences
  • Dynamic Allocation: Supports variable-length sequences efficiently

Core Optimizations

1. Fused Reshape and Block Write

Combines reshape operations with block writing to minimize kernel launch overhead:

__global__ void fused_reshape_and_block_write_kernel(
    const half* input_kv,            // Input KV cache
    half* output_blocks,             // Output block storage
    const int* block_table,          // Block table mapping
    const int* seq_lens,             // Sequence lengths
    const int batch_size,
    const int hidden_size,
    const int block_size,
    const int max_seq_len,
    const int num_blocks_per_seq
) {
    int tid = blockIdx.x * blockDim.x + threadIdx.x;
    int total_elements = batch_size * max_seq_len * hidden_size;
    
    if (tid >= total_elements) return;
    
    // Calculate position in sequence
    int seq_idx = tid / (max_seq_len * hidden_size);
    int pos_in_seq = (tid % (max_seq_len * hidden_size)) / hidden_size;
    int hidden_idx = tid % hidden_size;
    
    // Skip if position exceeds sequence length
    if (pos_in_seq >= seq_lens[seq_idx]) return;
    
    // Calculate block index and physical block ID
    int block_idx = pos_in_seq / block_size;
    int pos_in_block = pos_in_seq % block_size;
    int physical_block_id = block_table[seq_idx * num_blocks_per_seq + block_idx];
    
    // Calculate output address and perform write
    int output_idx = physical_block_id * block_size * hidden_size + 
                     pos_in_block * hidden_size + hidden_idx;
    output_blocks[output_idx] = input_kv[tid];
}

2. PagedAttention Main Kernel

The core attention computation kernel with block-based memory access:

__global__ void paged_attention_kernel(
    const half* query,               // Query matrix
    const half* key_blocks,          // Key blocks
    const half* value_blocks,        // Value blocks
    half* output,                    // Output attention
    const int* block_table,          // Block table
    const int* seq_lens,             // Sequence lengths
    const AttentionMetadata* metadata
) {
    int batch_idx = blockIdx.y;
    int head_idx = blockIdx.z;
    int tid = threadIdx.x;
    
    if (batch_idx >= metadata->batch_size || head_idx >= metadata->num_heads) return;
    
    int seq_len = seq_lens[batch_idx];
    int num_blocks = (seq_len + metadata->block_size - 1) / metadata->block_size;
    
    // Shared memory for key-value blocks
    extern __shared__ half shared_mem[];
    half* shared_key = shared_mem;
    half* shared_value = shared_mem + metadata->block_size * metadata->head_dim;
    
    // First pass: compute max attention scores
    float max_score = -INFINITY;
    for (int block_idx = 0; block_idx < num_blocks; block_idx++) {
        int physical_block_id = block_table[batch_idx * metadata->max_seq_len / metadata->block_size + block_idx];
        
        // Load key block to shared memory
        for (int i = tid; i < metadata->block_size * metadata->head_dim; i += blockDim.x) {
            shared_key[i] = key_blocks[physical_block_id * metadata->block_size * metadata->head_dim + i];
        }
        __syncthreads();
        
        // Compute attention scores for current block
        int block_start = block_idx * metadata->block_size;
        int block_end = min(block_start + metadata->block_size, seq_len);
        
        for (int k_pos = block_start; k_pos < block_end; k_pos++) {
            float score = 0.0f;
            for (int d = 0; d < metadata->head_dim; d++) {
                score += __half2float(query[batch_idx * metadata->num_heads * metadata->head_dim + 
                                           head_idx * metadata->head_dim + d]) *
                        __half2float(shared_key[(k_pos - block_start) * metadata->head_dim + d]);
            }
            score *= metadata->scale;
            max_score = fmaxf(max_score, score);
        }
        __syncthreads();
    }
    
    // Second pass: compute softmax and accumulate values
    float sum_exp = 0.0f;
    for (int block_idx = 0; block_idx < num_blocks; block_idx++) {
        int physical_block_id = block_table[batch_idx * metadata->max_seq_len / metadata->block_size + block_idx];
        
        // Load key-value blocks to shared memory
        for (int i = tid; i < metadata->block_size * metadata->head_dim; i += blockDim.x) {
            shared_key[i] = key_blocks[physical_block_id * metadata->block_size * metadata->head_dim + i];
            shared_value[i] = value_blocks[physical_block_id * metadata->block_size * metadata->head_dim + i];
        }
        __syncthreads();
        
        int block_start = block_idx * metadata->block_size;
        int block_end = min(block_start + metadata->block_size, seq_len);
        
        for (int k_pos = block_start; k_pos < block_end; k_pos++) {
            float score = 0.0f;
            for (int d = 0; d < metadata->head_dim; d++) {
                score += __half2float(query[batch_idx * metadata->num_heads * metadata->head_dim + 
                                           head_idx * metadata->head_dim + d]) *
                        __half2float(shared_key[(k_pos - block_start) * metadata->head_dim + d]);
            }
            score = expf(score * metadata->scale - max_score);
            sum_exp += score;
            
            // Accumulate to output
            for (int d = 0; d < metadata->head_dim; d++) {
                atomicAdd(&output[batch_idx * metadata->num_heads * metadata->head_dim + 
                                 head_idx * metadata->head_dim + d],
                         score * __half2float(shared_value[(k_pos - block_start) * metadata->head_dim + d]));
            }
        }
        __syncthreads();
    }
    
    // Normalize output
    for (int d = tid; d < metadata->head_dim; d += blockDim.x) {
        float val = __half2float(output[batch_idx * metadata->num_heads * metadata->head_dim + 
                                       head_idx * metadata->head_dim + d]);
        output[batch_idx * metadata->num_heads * metadata->head_dim + 
               head_idx * metadata->head_dim + d] = __float2half(val / sum_exp);
    }
}

3. Fused Block Copy

Optimizes copy-on-write operations by batching multiple block copies:

__global__ void fused_block_copy_kernel(
    const half* src_blocks,          // Source blocks
    half* dst_blocks,                // Destination blocks
    const int* copy_operations,      // Copy operations
    const int num_operations,
    const int block_size,
    const int hidden_size
) {
    int op_idx = blockIdx.x;
    int tid = threadIdx.x;
    
    if (op_idx >= num_operations) return;
    
    int src_block_id = copy_operations[op_idx * 3];
    int dst_block_id = copy_operations[op_idx * 3 + 1];
    int copy_size = copy_operations[op_idx * 3 + 2];
    
    int total_elements = copy_size * hidden_size;
    
    // Parallel block data copy
    for (int i = tid; i < total_elements; i += blockDim.x) {
        dst_blocks[dst_block_id * block_size * hidden_size + i] = 
            src_blocks[src_block_id * block_size * hidden_size + i];
    }
}

Memory Management

Block Table Structure

struct BlockTable {
    int* physical_block_ids;         // Physical block ID mapping
    int* logical_to_physical;        // Logical to physical mapping
    int* block_ref_counts;          // Block reference counts
    int max_blocks_per_seq;          // Maximum blocks per sequence
    int total_physical_blocks;       // Total physical blocks
    int block_size;                  // Size of each block
};

Block Allocation/Deallocation

__global__ void block_allocation_kernel(
    BlockTable* table,
    const int* seq_ids,
    const int* logical_block_ids,
    int* allocated_blocks,
    const int num_allocations
) {
    int tid = blockIdx.x * blockDim.x + threadIdx.x;
    
    if (tid >= num_allocations) return;
    
    int seq_id = seq_ids[tid];
    int logical_block_id = logical_block_ids[tid];
    
    // Find free physical block
    for (int i = 0; i < table->total_physical_blocks; i++) {
        if (atomicCAS(&table->block_ref_counts[i], 0, 1) == 0) {
            // Allocate block
            table->physical_block_ids[seq_id * table->max_blocks_per_seq + logical_block_id] = i;
            allocated_blocks[tid] = i;
            return;
        }
    }
    
    // Allocation failed
    allocated_blocks[tid] = -1;
}

Performance Metrics

  • Memory Utilization: >96% (vs 60-80% traditional)
  • Inference Throughput: Up to 30x improvement
  • Memory Waste: <4%
  • Context Length: Supports up to 131,072 tokens
  • Batch Processing: Efficient handling of variable-length sequences

🧠 Query-Aware Cache Selection for Efficient LLM Inference

Overview

Query-Aware Cache Selection is an advanced optimization technique that intelligently manages KV cache based on query characteristics and access patterns. This approach significantly improves inference efficiency by predicting which cache entries are most likely to be accessed and optimizing memory allocation accordingly.

Core Components

1. Query Analysis Engine

Analyzes incoming queries to determine their characteristics and cache requirements:

__global__ void query_analysis_kernel(
    const half* queries,             // Input queries
    float* query_complexity,         // Output complexity scores
    int* cache_requirements,          // Cache requirements
    const int batch_size,
    const int seq_len,
    const int hidden_size
) {
    int tid = blockIdx.x * blockDim.x + threadIdx.x;
    int total_elements = batch_size * seq_len * hidden_size;
    
    if (tid >= total_elements) return;
    
    int batch_idx = tid / (seq_len * hidden_size);
    int pos_in_seq = (tid % (seq_len * hidden_size)) / hidden_size;
    int hidden_idx = tid % hidden_size;
    
    // Analyze query complexity based on attention patterns
    float complexity_score = 0.0f;
    
    // Compute attention entropy (higher entropy = more complex)
    for (int d = 0; d < hidden_size; d++) {
        float val = __half2float(queries[tid]);
        complexity_score += val * val;
    }
    
    // Store complexity score
    if (hidden_idx == 0) {
        atomicAdd(&query_complexity[batch_idx * seq_len + pos_in_seq], complexity_score);
    }
    
    // Determine cache requirements based on complexity
    if (complexity_score > 0.5f) {
        cache_requirements[batch_idx * seq_len + pos_in_seq] = 2; // High cache
    } else if (complexity_score > 0.2f) {
        cache_requirements[batch_idx * seq_len + pos_in_seq] = 1; // Medium cache
    } else {
        cache_requirements[batch_idx * seq_len + pos_in_seq] = 0; // Low cache
    }
}

2. Cache Selection Strategy

Implements intelligent cache selection based on query analysis:

__global__ void cache_selection_kernel(
    const float* query_complexity,    // Query complexity scores
    const int* cache_requirements,    // Cache requirements
    const int* access_history,        // Historical access patterns
    int* selected_cache_blocks,       // Selected cache blocks
    const int batch_size,
    const int seq_len,
    const int num_cache_blocks,
    const float cache_threshold
) {
    int tid = blockIdx.x * blockDim.x + threadIdx.x;
    int total_positions = batch_size * seq_len;
    
    if (tid >= total_positions) return;
    
    int batch_idx = tid / seq_len;
    int pos_in_seq = tid % seq_len;
    
    float complexity = query_complexity[tid];
    int requirement = cache_requirements[tid];
    int history = access_history[tid];
    
    // Calculate cache selection score
    float selection_score = complexity * 0.4f + 
                           requirement * 0.3f + 
                           (history / 100.0f) * 0.3f;
    
    // Select cache blocks based on score
    if (selection_score > cache_threshold) {
        // High priority - allocate multiple cache blocks
        for (int i = 0; i < min(requirement + 1, 4); i++) {
            int block_id = (batch_idx * seq_len + pos_in_seq) * 4 + i;
            if (block_id < num_cache_blocks) {
                selected_cache_blocks[block_id] = 1;
            }
        }
    } else if (selection_score > cache_threshold * 0.5f) {
        // Medium priority - allocate single cache block
        int block_id = batch_idx * seq_len + pos_in_seq;
        if (block_id < num_cache_blocks) {
            selected_cache_blocks[block_id] = 1;
        }
    } else {
        // Low priority - no dedicated cache
        selected_cache_blocks[tid] = 0;
    }
}

3. Adaptive Cache Management

Dynamically adjusts cache allocation based on runtime performance:

__global__ void adaptive_cache_management_kernel(
    const int* cache_hit_rates,       // Cache hit rates
    const int* access_frequencies,    // Access frequencies
    int* cache_allocation,            // Cache allocation
    const int num_cache_blocks,
    const int batch_size,
    const float hit_rate_threshold,
    const int frequency_threshold
) {
    int tid = blockIdx.x * blockDim.x + threadIdx.x;
    
    if (tid >= num_cache_blocks) return;
    
    int hit_rate = cache_hit_rates[tid];
    int frequency = access_frequencies[tid];
    
    // Adaptive cache allocation based on performance metrics
    if (hit_rate < hit_rate_threshold && frequency > frequency_threshold) {
        // Increase cache allocation for frequently accessed, low-hit blocks
        cache_allocation[tid] = min(cache_allocation[tid] + 1, 4);
    } else if (hit_rate > hit_rate_threshold * 1.5f && frequency < frequency_threshold * 0.5f) {
        // Decrease cache allocation for high-hit, infrequently accessed blocks
        cache_allocation[tid] = max(cache_allocation[tid] - 1, 0);
    }
    
    // Update access frequency
    atomicAdd(&access_frequencies[tid], 1);
}

Integration with TinyServe PagedAttention

The Query-Aware Cache Selection seamlessly integrates with TinyServe's optimized PagedAttention kernels:

__global__ void tinyserve_query_aware_paged_attention_kernel(
    const half* query,               // Query matrix
    const half* key_blocks,          // Key blocks
    const half* value_blocks,        // Value blocks
    half* output,                    // Output attention
    const int* block_table,          // Block table
    const int* seq_lens,             // Sequence lengths
    const int* cache_selection,      // Cache selection
    const TinyServeAttentionMetadata* metadata
) {
    int batch_idx = blockIdx.y;
    int head_idx = blockIdx.z;
    int tid = threadIdx.x;
    int warp_id = tid / WARP_SIZE;
    int lane_id = tid % WARP_SIZE;
    
    if (batch_idx >= metadata->batch_size || head_idx >= metadata->num_heads) return;
    
    int seq_len = seq_lens[batch_idx];
    int num_blocks = (seq_len + metadata->block_size - 1) / metadata->block_size;
    
    // Shared memory for FlashAttention blocks
    extern __shared__ half shared_mem[];
    half* shared_key = shared_mem;
    half* shared_value = shared_mem + FLASH_ATTENTION_BLOCK_M * metadata->head_dim;
    half* shared_query = shared_mem + 2 * FLASH_ATTENTION_BLOCK_M * metadata->head_dim;
    
    // Initialize output
    for (int d = tid; d < metadata->head_dim; d += blockDim.x) {
        output[batch_idx * metadata->num_heads * metadata->head_dim + 
               head_idx * metadata->head_dim + d] = __float2half(0.0f);
    }
    __syncthreads();
    
    // Load query block to shared memory
    for (int i = tid; i < FLASH_ATTENTION_BLOCK_M * metadata->head_dim; i += blockDim.x) {
        shared_query[i] = query[batch_idx * metadata->num_heads * metadata->head_dim + 
                               head_idx * metadata->head_dim + i];
    }
    __syncthreads();
    
    // Process blocks with query-aware cache selection
    float max_score = -INFINITY;
    float sum_exp = 0.0f;
    
    for (int block_idx = 0; block_idx < num_blocks; block_idx++) {
        int physical_block_id = block_table[batch_idx * metadata->max_seq_len / metadata->block_size + block_idx];
        
        // Check cache selection for this block
        int cache_priority = cache_selection[batch_idx * seq_len + block_idx * metadata->block_size];
        
        // Skip low-priority blocks if cache is full
        if (cache_priority == 0 && block_idx > num_blocks / 2) {
            continue;
        }
        
        // Coalesced load of key-value blocks
        coalesced_load(
            key_blocks + physical_block_id * metadata->block_size * metadata->head_dim,
            shared_key,
            metadata->block_size * metadata->head_dim,
            tid,
            blockDim.x
        );
        
        coalesced_load(
            value_blocks + physical_block_id * metadata->block_size * metadata->head_dim,
            shared_value,
            metadata->block_size * metadata->head_dim,
            tid,
            blockDim.x
        );
        __syncthreads();
        
        // Compute attention scores with warp-level optimization
        int block_start = block_idx * metadata->block_size;
        int block_end = min(block_start + metadata->block_size, seq_len);
        
        float local_max = -INFINITY;
        for (int k_pos = block_start; k_pos < block_end; k_pos++) {
            float score = 0.0f;
            for (int d = 0; d < metadata->head_dim; d++) {
                score += __half2float(shared_query[d]) *
                        __half2float(shared_key[(k_pos - block_start) * metadata->head_dim + d]);
            }
            score *= metadata->scale;
            local_max = fmaxf(local_max, score);
        }
        
        // Warp-level reduction for max
        local_max = warp_reduce_max(local_max);
        if (lane_id == 0) {
            max_score = fmaxf(max_score, local_max);
        }
        __syncthreads();
        
        // Compute softmax and accumulate
        float local_sum = 0.0f;
        for (int k_pos = block_start; k_pos < block_end; k_pos++) {
            float score = 0.0f;
            for (int d = 0; d < metadata->head_dim; d++) {
                score += __half2float(shared_query[d]) *
                        __half2float(shared_key[(k_pos - block_start) * metadata->head_dim + d]);
            }
            score = expf(score * metadata->scale - max_score);
            local_sum += score;
            
            // Accumulate to output
            for (int d = 0; d < metadata->head_dim; d++) {
                atomicAdd(&output[batch_idx * metadata->num_heads * metadata->head_dim + 
                                 head_idx * metadata->head_dim + d],
                         score * __half2float(shared_value[(k_pos - block_start) * metadata->head_dim + d]));
            }
        }
        
        // Warp-level reduction for sum
        local_sum = warp_reduce_sum(local_sum);
        if (lane_id == 0) {
            sum_exp += local_sum;
        }
        __syncthreads();
    }
    
    // Normalize output
    for (int d = tid; d < metadata->head_dim; d += blockDim.x) {
        float val = __half2float(output[batch_idx * metadata->num_heads * metadata->head_dim + 
                                       head_idx * metadata->head_dim + d]);
        output[batch_idx * metadata->num_heads * metadata->head_dim + 
               head_idx * metadata->head_dim + d] = __float2half(val / sum_exp);
    }
}

⚑ TinyServe Optimized Kernels

Overview

TinyServe provides enhanced CUDA kernels that integrate FlashAttention with PagedAttention, delivering superior performance for LLM serving. These kernels combine the memory efficiency of PagedAttention with the computational efficiency of FlashAttention.

Key Features

  • FlashAttention Integration: Combines FlashAttention's memory efficiency with PagedAttention's block management
  • Advanced Memory Coalescing: Optimized memory access patterns for maximum bandwidth utilization
  • Warp-level Optimizations: Maximum GPU utilization through warp-level reductions
  • Dynamic Workload Balancing: Intelligent distribution of work across GPU warps
  • LRU Cache Management: Intelligent block placement with access pattern learning

Core Optimizations

1. FlashAttention with PagedAttention

__global__ void tinyserve_flash_paged_attention_kernel(
    const half* query,               // Query matrix
    const half* key_blocks,          // Key blocks
    const half* value_blocks,        // Value blocks
    half* output,                    // Output attention
    const int* block_table,          // Block table
    const int* seq_lens,             // Sequence lengths
    const TinyServeAttentionMetadata* metadata
) {
    // FlashAttention-style tiling with PagedAttention block management
    // Warp-level reductions for maximum performance
    // Coalesced memory access for optimal bandwidth
}

2. Advanced Block Allocation

__global__ void tinyserve_advanced_block_allocation_kernel(
    TinyServeBlockTable* table,
    const int* seq_ids,
    const int* logical_block_ids,
    int* allocated_blocks,
    const int* access_frequencies
) {
    // LRU cache with access pattern optimization
    // Intelligent block placement based on usage patterns
    // Dynamic cache size adjustment
}

3. Intelligent Memory Compaction

__global__ void tinyserve_intelligent_memory_compaction_kernel(
    half* blocks,
    const int* old_to_new_mapping,
    const int* block_weights,
    const int compaction_threshold
) {
    // Weight-based memory compaction
    // Only compact low-importance blocks
    // Preserve frequently accessed data
}

Performance Benefits

  • Memory Utilization: >96% (vs 60-80% traditional)
  • Inference Throughput: Up to 30x improvement over baseline
  • Cache Hit Rate: 15-25% improvement with Query-Aware Cache Selection
  • Memory Waste: <4%
  • Context Length: Supports up to 131K tokens
  • Batch Processing: Efficient handling of variable-length sequences

Integration Example

// Complete TinyServe inference pipeline
void tinyserve_inference_pipeline(
    const half* queries,
    const int* seq_lens,
    TinyServeBlockTable* block_table,
    TinyServeAttentionMetadata* metadata
) {
    // Step 1: Query analysis for cache selection
    tinyserve_launch_query_analysis(queries, query_complexity, cache_requirements, stream);
    
    // Step 2: Dynamic workload balancing
    tinyserve_launch_dynamic_workload_balancing(seq_lens, work_distribution, stream);
    
    // Step 3: Advanced block allocation
    tinyserve_launch_advanced_block_allocation(block_table, seq_ids, logical_blocks, stream);
    
    // Step 4: FlashAttention with PagedAttention
    tinyserve_launch_flash_paged_attention(queries, key_blocks, value_blocks, output, stream);
    
    // Step 5: Adaptive cache management
    tinyserve_launch_adaptive_cache_management(cache_hit_rates, access_frequencies, stream);
}

πŸ› οΈ Installation & Usage

Requirements

# CUDA Environment
CUDA >= 11.0
cuDNN >= 8.0
NVCC (NVIDIA CUDA Compiler)

# Build Tools
GCC >= 7.0
Make

Quick Start

# Clone repository
git clone https://github.com/FastLM/TinyServe.git
cd TinyServe

# Check CUDA installation
make check-cuda

# Build everything
make all

# Run example
make run

# Run benchmark
make benchmark

# Test compilation
make test

Advanced Usage

# Compile with specific CUDA architecture
make CUDA_ARCH="-arch=sm_80" all

# Profile performance
make profile

# Memory analysis
make memcheck

# Install system-wide
make install

Usage Examples

Basic TinyServe Usage

#include "tinyserve_kernels.h"

// Initialize TinyServe
TinyServeBlockTable block_table;
TinyServeAttentionMetadata metadata;
TinyServeKernelConfig config;

// Configure metadata
metadata.batch_size = 8;
metadata.num_heads = 32;
metadata.head_dim = 128;
metadata.use_flash_attention = true;

// Initialize block table
tinyserve_init_block_table(&block_table, max_blocks_per_seq, 
                          total_physical_blocks, block_size, cache_size);

// Launch optimized attention
tinyserve_launch_flash_paged_attention(query, key_blocks, value_blocks, 
                                      output, block_table, seq_lens, 
                                      &metadata, stream);

Query-Aware Cache Selection

// Analyze query complexity
tinyserve_launch_query_analysis(queries, query_complexity, 
                                cache_requirements, stream);

// Select cache blocks
tinyserve_launch_cache_selection(query_complexity, cache_requirements,
                                 access_history, selected_cache_blocks, stream);

// Adaptive cache management
tinyserve_launch_adaptive_cache_management(cache_hit_rates, access_frequencies,
                                          cache_allocation, stream);

File Structure

TinyServe/
β”œβ”€β”€ README.md                    # This file
β”œβ”€β”€ vllm_kernels.cu             # Complete CUDA kernel implementation
β”œβ”€β”€ tinyserve_kernels.h          # Header file for TinyServe kernels
β”œβ”€β”€ tinyserve_example.cu         # Example implementation
β”œβ”€β”€ Makefile                     # Build configuration
β”œβ”€β”€ LICENSE                      # MIT License
β”œβ”€β”€ .gitignore                   # Git ignore file
β”œβ”€β”€ requirements.txt             # Python dependencies (optional)
β”œβ”€β”€ examples/                    # Example implementations
β”‚   └── cuda_kernels/           # CUDA kernel examples
β”‚       └── paged_attention_example.py  # Python example
└── tests/                      # Unit tests
    β”œβ”€β”€ test_tinyserve_kernels.cu  # CUDA test suite
    └── test_tinyserve_kernels.py  # Python test suite

πŸ“š References

About

[ACM MM 2025 Oral] TinyServe Page Allocation Kernel Optimization

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published