silvic

Train and serve gradient-boosted trees in Rust with LightGBM-compatible model files.

Training runs on CPU, Apple Metal, NVIDIA CUDA and AWS Neuron. Serving runs on CPU, CUDA and Neuron (float32).

Training

End-to-end seconds

  • silvic · CUDA · native single27.26 s
  • silvic · CPU · Ray 2 workers255.35 s
  • LightGBM · CPU · Ray 2 workers291.87 s
  • silvic · CPU · native single400.22 s
  • LightGBM · CPU · native single469.94 s

Serving

Host input to host output · milliseconds

  • nvForest · CUDA (f32)0.542 ms
  • silvic.serve · CUDA (f32)0.609 ms
  • silvic.serve · CUDA (f64)0.979 ms
  • nvForest · CUDA (f64)1.329 ms
  • silvic.serve · CPU (AVX2)2.121 ms
  • lleaves 1.3 · CPU7.416 ms
  • LightGBM 4.7 · CPU26.986 ms
Benchmark details

Training. 1M synthetic learning-to-rank rows and 100k validation rows, 100 numerical float32 features, queries of 50; 1,000 lambdarank trees, 53 leaves, 63 bins. All engines ran sequentially on the same two Xeon Platinum 8259CL / Tesla T4 nodes. Native rows time direct single-process dataset construction, binning, fit, validation and serialization; Ray rows also include partitioning, transfer, actor or Trainer setup, TCP and teardown. Two CPU threads per process and one GPU per CUDA process. Median of three fits after a one-tree warmup; data generation and cluster provisioning excluded.

Serving. One fixed 200-tree, 53-leaf model with 100 features. Median of 100 calls cycling through eight held-out 3,000-row batches after 10 warmups. Engines ran sequentially on one Xeon Platinum 8259CL / Tesla T4 host; CPU engines used two threads. Host input to host output includes CUDA transfers, allocation and synchronization; model loading and compilation excluded.

Python API

pip install silvic
pip install "silvic[ray]"
import silvic

train_set = silvic.Dataset(X, label=y, group=query_sizes)
booster = silvic.train({"objective": "lambdarank"}, train_set, num_boost_round=1500)
booster.save_model("model.txt")
import silvic.ray

booster = silvic.ray.train(
    params,
    ray.data.read_parquet("s3://bucket/train/"),
    label_column="label",
    group_column="query_id",
    num_workers=5,
    use_gpu=True,
)
import silvic.serve

predictor = silvic.serve.Predictor("model.txt")  # LightGBM 3.x or 4.x model
scores = predictor.predict(candidates, num_threads=1)