PluginBench
Skill
Pass
Audit score 90

domain-ml

actionbook/rust-skills

Build ML/AI applications in Rust with efficient tensor operations, GPU acceleration, and model inference.

What is domain-ml?

This skill provides domain constraints and design patterns for machine learning and AI applications in Rust. Use it when building inference servers, training pipelines, or data processing systems that require efficient memory usage, GPU utilization, and model portability.

  • Design memory-efficient tensor operations using zero-copy references and views
  • Implement GPU-accelerated inference with batching for optimal throughput
  • Load and cache ML models once using singleton patterns (OnceLock)
  • Handle standard model formats (ONNX) for portability between training and deployment
  • Build async data pipelines to parallelize loading and computation
  • Apply streaming and batching patterns for large-scale data processing

How to install domain-ml

npx skills add https://github.com/actionbook/rust-skills --skill domain-ml
Claude Code
Cursor
Windsurf
Cline

How to use domain-ml

  1. 1.Identify your use case: inference-only (use tract), training+inference (use candle/burn), or PyTorch models (use tch-rs)
  2. 2.Load your model once at startup using OnceLock to avoid repeated initialization overhead
  3. 3.Structure input data into batches to maximize GPU utilization and throughput
  4. 4.Use ndarray views and references instead of cloning tensors to minimize memory copies
  5. 5.Implement async data loading pipelines to keep GPU busy while preparing next batch
  6. 6.Apply streaming iterators for datasets larger than available memory

Use cases

Good for
  • Building inference servers that load ONNX models once and serve predictions with batched requests
  • Processing large datasets with streaming iterators to avoid memory copies
  • Deploying PyTorch models trained in Python as efficient Rust services using tch-rs or tract
  • Implementing GPU-accelerated ML pipelines with async data loading parallel to compute
  • Creating reproducible ML systems with seeded randomness and deterministic operations
Who it's for
  • Rust backend engineers building ML inference services
  • Data engineers optimizing ML data pipelines for performance
  • ML practitioners deploying Python-trained models to production Rust systems
  • Systems programmers requiring memory-efficient tensor operations

domain-ml FAQ

Should I use tract, candle, burn, or tch-rs?

Use tract for lightweight ONNX inference, candle/burn for pure-Rust training+inference, and tch-rs for direct PyTorch model bindings.

How do I avoid memory overhead with large tensors?

Use ndarray views and references instead of cloning, apply in-place operations, and stream data through iterators rather than loading everything into memory.

Why is batching important for GPU inference?

GPU kernels have launch overhead; batching amortizes this cost across multiple samples, significantly improving throughput compared to single-sample inference.

How should I load ML models in a server?

Use OnceLock or similar lazy-initialization patterns to load the model once at startup and reuse it across all requests, avoiding expensive reloads.

Can I use Python-trained models in Rust?

Yes—export models to ONNX format and load with tract or candle, or use tch-rs for direct PyTorch model bindings.

Full instructions (SKILL.md)

Source of truth, from actionbook/rust-skills.


name: domain-ml description: "Use when building ML/AI apps in Rust. Keywords: machine learning, ML, AI, tensor, model, inference, neural network, deep learning, training, prediction, ndarray, tch-rs, burn, candle, 机器学习, 人工智能, 模型推理" user-invocable: false

Machine Learning Domain

Layer 3: Domain Constraints

Domain Constraints → Design Implications

Domain RuleDesign ConstraintRust Implication
Large dataEfficient memoryZero-copy, streaming
GPU accelerationCUDA/Metal supportcandle, tch-rs
Model portabilityStandard formatsONNX
Batch processingThroughput over latencyBatched inference
Numerical precisionFloat handlingndarray, careful f32/f64
ReproducibilityDeterministicSeeded random, versioning

Critical Constraints

Memory Efficiency

RULE: Avoid copying large tensors
WHY: Memory bandwidth is bottleneck
RUST: References, views, in-place ops

GPU Utilization

RULE: Batch operations for GPU efficiency
WHY: GPU overhead per kernel launch
RUST: Batch sizes, async data loading

Model Portability

RULE: Use standard model formats
WHY: Train in Python, deploy in Rust
RUST: ONNX via tract or candle

Trace Down ↓

From constraints to design (Layer 2):

"Need efficient data pipelines"
    ↓ m10-performance: Streaming, batching
    ↓ polars: Lazy evaluation

"Need GPU inference"
    ↓ m07-concurrency: Async data loading
    ↓ candle/tch-rs: CUDA backend

"Need model loading"
    ↓ m12-lifecycle: Lazy init, caching
    ↓ tract: ONNX runtime

Use Case → Framework

Use CaseRecommendedWhy
Inference onlytract (ONNX)Lightweight, portable
Training + inferencecandle, burnPure Rust, GPU
PyTorch modelstch-rsDirect bindings
Data pipelinespolarsFast, lazy eval

Key Crates

PurposeCrate
Tensorsndarray
ONNX inferencetract
ML frameworkcandle, burn
PyTorch bindingstch-rs
Data processingpolars
Embeddingsfastembed

Design Patterns

PatternPurposeImplementation
Model loadingOnce, reuseOnceLock<Model>
BatchingThroughputCollect then process
StreamingLarge dataIterator-based
GPU asyncParallelismData loading parallel to compute

Code Pattern: Inference Server

use std::sync::OnceLock;
use tract_onnx::prelude::*;

static MODEL: OnceLock<SimplePlan<TypedFact, Box<dyn TypedOp>, Graph<TypedFact, Box<dyn TypedOp>>>> = OnceLock::new();

fn get_model() -> &'static SimplePlan<...> {
    MODEL.get_or_init(|| {
        tract_onnx::onnx()
            .model_for_path("model.onnx")
            .unwrap()
            .into_optimized()
            .unwrap()
            .into_runnable()
            .unwrap()
    })
}

async fn predict(input: Vec<f32>) -> anyhow::Result<Vec<f32>> {
    let model = get_model();
    let input = tract_ndarray::arr1(&input).into_shape((1, input.len()))?;
    let result = model.run(tvec!(input.into()))?;
    Ok(result[0].to_array_view::<f32>()?.iter().copied().collect())
}

Code Pattern: Batched Inference

async fn batch_predict(inputs: Vec<Vec<f32>>, batch_size: usize) -> Vec<Vec<f32>> {
    let mut results = Vec::with_capacity(inputs.len());

    for batch in inputs.chunks(batch_size) {
        // Stack inputs into batch tensor
        let batch_tensor = stack_inputs(batch);

        // Run inference on batch
        let batch_output = model.run(batch_tensor).await;

        // Unstack results
        results.extend(unstack_outputs(batch_output));
    }

    results
}

Common Mistakes

MistakeDomain ViolationFix
Clone tensorsMemory wasteUse views
Single inferenceGPU underutilizedBatch processing
Load model per requestSlowSingleton pattern
Sync data loadingGPU idleAsync pipeline

Trace to Layer 1

ConstraintLayer 2 PatternLayer 1 Implementation
Memory efficiencyZero-copyndarray views
Model singletonLazy initOnceLock<Model>
Batch processingChunked iterationchunks() + parallel
GPU asyncConcurrent loadingtokio::spawn + GPU

Related Skills

WhenSee
Performancem10-performance
Lazy initializationm12-lifecycle
Async patternsm07-concurrency
Memory efficiencym01-ownership