Model or dataset
kossisoroyce/timber avatar
kossisoroyce/timber

Timber compiles XGBoost and scikit-learn models to C99, then serves them over an Ollama-shaped HTTP API

Ollama for classical ML models. AOT compiler that turns XGBoost, LightGBM, scikit-learn, CatBoost & ONNX models into native C99 inference code. One command to load, one command to serve. 336x faster than Python inference.

688 stars23 forksPythonNOASSERTION

At a glance

What is it?
Timber is an ahead-of-time compiler that turns classical ML model files into self-contained C99 inference artifacts with no runtime dependencies, wrapped in a `timber serve` HTTP server. The design is coherent for edge and latency-sensitive deployments, but the README's headline numbers and the `timber accel` feature list are not reproducible from the material provided, and the licence metadata is inconsistent.
Who is it for?
Adopt Timber if your inference path is a tree ensemble or linear model, you control the build machine, and you want a shared object instead of a Python process. Do not adopt it if your model is a neural network, if you need to retrain and redeploy continuously, or if you cannot accept a NOASSERTION licence field on the repository.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 152 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The problem is the Python process, not the model

A gradient-boosted tree ensemble evaluates in microseconds. The Python process that hosts it does not. Importing XGBoost or scikit-learn pulls in NumPy, loads a shared library, and allocates interpreter state before a single row is scored. In a transaction path where the model is one step in a longer pipeline, that startup and marshalling cost can dominate the arithmetic. Timber's answer is to remove Python from the inference path entirely: the model file is parsed at build time, translated into C99, and compiled to a shared library that the serving layer loads through `ctypes`. The README frames the target audience as fraud and risk teams working in sub-millisecond transaction paths, edge and IoT deployments shipping to gateways or Cortex-M targets, regulated industries that need deterministic and auditable artifacts, and platform teams that want the Python serving stack off the critical path. That is a narrower audience than the tagline suggests. If your model is a transformer, Timber is not aimed at you. The supported list is tree ensembles, linear models, SVMs, k-NN, Naive Bayes, GPR, Isolation Forest, and URDF robot descriptions.

Five stages: parse, optimize, emit, compile, serve

The pipeline is documented as a sequence with a clear boundary between build-time and run-time work. The parser reads the native model format (`.json`, `.pkl`, `.txt`, `.onnx` are the extensions named) into a framework-agnostic intermediate representation the README calls Timber IR, described as a typed AST. The optimizer then applies named passes: dead-leaf elimination, threshold quantization, constant-feature folding, and branch sorting. The example console output reports how many passes actually fired on a given model, showing `3/5 passes applied` for the breast cancer example, which implies a five-pass pipeline where some passes are conditional. The C99 emitter splits output across three files: `model.c` for inference logic, `model.h` for the public API, and `model_data.c` for tree data. `gcc` or `clang` then produces `model.so`. The README states the generated code has no dynamic allocation and no recursion, which is what makes the artifact plausible on embedded targets. Serving is a separate concern: an HTTP layer wraps the compiled binary and exposes `/api/predict`, `/api/models`, and `/api/health` on port 11434, the same port Ollama uses. The Ollama-compatible framing is a deliberate integration choice, not an architectural one.

Getting a model compiled and answering requests

Installation is a single package from PyPI: `pip install timber-compiler`. The README gives two entry paths. The first compiles and serves directly from a URL in one command: `timber serve https://raw.githubusercontent.com/kossisoroyce/timber/main/examples/breast_cancer_model.json`. The second separates loading from serving, which is what you would do in a build pipeline: `timber load fraud_model.json --name fraud-detector` followed by `timber serve fraud-detector`. The `--name` flag is what registers the model under a lookup key for the server. Prediction is a POST to `http://localhost:11434/api/predict` with a JSON body containing a `model` field and an `inputs` array of arrays. The response shape in the README is `{"model": ..., "outputs": [[0.9971]], "n_samples": 1}`, so outputs are nested one level deep even for a single row. The example model is a 50-tree, 30-feature binary:logistic XGBoost classifier, and the reported compiled binary is 47.9 KB with 169 lines of generated C99. Treat those numbers as a property of that specific model, not as a general artifact-size expectation.

The accel backend is the part you cannot verify from the README

Timber ships a second backend, `timber accel`, which the README says emits AVX2, AVX-512, NEON, SVE and RVV SIMD variants, CUDA, Metal and OpenCL GPU kernels, Xilinx and Intel FPGA HLS, and Cortex-M, ESP32 and STM32 embedded targets. The same paragraph lists WCET analysis, DO-178C, ISO-26262 and IEC-62304 certification reports, Ed25519 artifact signing, AES-256-GCM encryption, air-gapped deployment bundles, and ROS 2, PX4 and gRPC server generators. All of it is described as arriving in one `pip install`. That is an unusually wide surface for a project whose latest release is v0.6.0, with v0.4.0 and v0.5.0 landing in the six weeks before it. The README offers no per-target validation status, no statement of which targets have been exercised on real silicon, and no indication of which certification reports are templates versus artefacts produced by a qualified toolchain. If your use case depends on DO-178C output or on an FPGA build, verify that specific path against your own toolchain before you design around it. The core C99 emitter and the HTTP server are the parts the documentation actually demonstrates end to end.

Where Timber is the wrong tool

The supported model list is the first boundary. Tree ensembles, linear models, SVMs, k-NN, Naive Bayes, GPR and Isolation Forest are covered. Neural networks are not on that list, so a PyTorch or TensorFlow model has no path through this compiler. The second boundary is retraining cadence. Because compilation is an explicit build step that produces a shared library, every model update is a rebuild and a redeploy. A team that retrains hourly and expects the serving layer to pick up new weights has a workflow mismatch; Timber wants models that change on the scale of releases, not requests. The third boundary is the parser. Model formats are not stable across library versions, and the README does not state which XGBoost, LightGBM, scikit-learn or CatBoost versions the parser has been validated against. A `.pkl` file is a pickled Python object, which makes it the most version-fragile of the listed inputs; a model serialized by one scikit-learn minor version may not load cleanly in another. The fourth is the licence field. The repository metadata reports NOASSERTION while the README badge and the License section point at Apache 2.0. Those two signals disagree, and the discrepancy matters if you are in a regulated industry that gates on licence review. Check the `LICENSE` file in the repository before you build a compliance case on it.

Compared with keeping the model in Python

The obvious alternative is to keep the model where it was trained and serve it with FastAPI or Flask behind a worker process. That approach has real advantages Timber gives up. You keep the full framework available at inference time, so you can switch models, add preprocessing, and use any estimator the library supports without a compile step. You also avoid a C toolchain in your build. What you pay is the Python process: interpreter startup, import cost, and the per-call overhead of moving arrays between Python and native code. Timber's claim is that removing that layer is worth the build complexity. The README asserts roughly 2 microseconds per single-sample inference and roughly 336x faster than Python XGBoost, but those figures come from the project's own documentation and the README does not include the benchmark configuration, hardware, or methodology needed to reproduce them. The honest comparison is not the headline ratio; it is whether your request path is actually dominated by Python overhead. If your service batches hundreds of rows per call, the per-call overhead amortizes and the gap narrows considerably. If you score one row per transaction and the rest of the path is already in C or Go, the gap is the whole point.

Maintenance cost and what to check first

There is no runtime dependency to patch, which is the maintenance story Timber is selling: a compiled `model.so` does not need a NumPy upgrade, a security advisory fix, or a Python version bump. That is genuine and it is the strongest argument for the approach in long-lived deployments. The cost moves to the build side. You now own a compiler pipeline, a C toolchain, and a generated artifact that must be regenerated whenever the model is retrained. The release cadence visible in the material is three releases in roughly six weeks (v0.4.0 in March, v0.5.0 in March, v0.6.0 in April), which is fast enough that pinning a version in your build is worth doing rather than tracking the latest. On licensing, the README and the badge say Apache 2.0, which is permissive and carries a patent grant, but the repository metadata says NOASSERTION. This is not legal advice; resolve the discrepancy with whoever reviews licences on your team, using the `LICENSE` file rather than the badge. The concrete first step is to run `timber load` against your own exported model, not the breast cancer example, and read the generated `model.c` and `model_data.c` before you trust the pipeline with a production artifact.

Editorial conclusion

Adopt Timber if your inference path is a tree ensemble or linear model, you control the build machine, and you want a shared object instead of a Python process. Do not adopt it if your model is a neural network, if you need to retrain and redeploy continuously, or if you cannot accept a NOASSERTION licence field on the repository. Before committing, run `timber load` on your own model file and inspect the generated `model.c`, `model.h` and `model_data.c` to confirm the parser handles your framework's exact export format, then benchmark the resulting `model.so` against your current Python path on your own hardware rather than trusting the README's 336x figure.

Official sources

  1. Issues
  2. kossisoroyce/timber on GitHub
  3. Project website
  4. README
  5. Releases
Community notes

Community notes