Fast because it is native

Templates compile to bytecode and run on a VM written in Rust, cached per template, so a cold request costs the same as a warm one. There is no host VM to boot, no bytecode to warm and no garbage collector to tune. The whole engine, under load, fits in about sixty megabytes.

2–3.5×throughput vs Lucee 7Apache Bench, 8s runs
~60 MBresident under loadvs ~560 MB warmed
instantcold startvs roughly 15s for Lucee
25,855req/s sustained100 clients, keep-alive

“Hello World”, one page, one machine

A single .cfm page in --production mode against a warmed Lucee 7 on the same machine (Apple M-series), same page, Apache Bench, eight-second runs. Requests per second, higher is better.

Requests per second, RustCFML and Lucee 7.0
Concurrency RustCFML Lucee 7.0 Gap RustCFML keep-alive Lucee keep-alive
-c 1 1,9081,205 3,1181,625
-c 10 5,4662,648 21,7168,125
-c 50 6,9833,503 25,8338,085
-c 100 7,5283,107 25,8557,419

Keep-alive is where the gap widens. Both engines improve with persistent connections, but RustCFML keeps scaling: at one hundred concurrent clients it holds around 25,800 requests per second while Lucee falls back to roughly 7,400. If your traffic arrives through a reverse proxy with an upstream keep-alive pool, this is the column that describes your production traffic.

Footprint and start-up
RustCFMLLucee 7.0
Memory (RSS, under load)~60 MB~560 MB
Time to first requestinstant~15 s
Artifact1 binaryJVM + runtime
Query-of-queries: 1M rows, ms (lower is better)
EngineTotalSpeedup
RustCFML1,116baseline
BoxLang 1.141,3681.23×
Lucee 7.0.47,8847.1×

Built for performance

Compile, then run

Tags are preprocessed to CFScript, then lexed, parsed and compiled to stack-based bytecode. The bytecode is cached per template, so the second request for a page runs no compiler at all.

No VM under the VM

There is no JVM, so no warm-up, no garbage-collection pauses and no container startup. The process is the runtime, and it is tens of megabytes.

Warm caches in production mode

--production caches the Application.cfc resolution, URL-to-file resolution and bytecode permanently instead of checking modification times per request. Worth 3–4×.

Parallel SQL in Rust

Query-of-queries is a real SQL engine in crates/cfml-qoq, parallelised across cores with rayon, not an embedded HSQLDB doing string work.

Performance testing

They are the project’s own, published in the README, on one Apple M-series machine, on a page that does almost nothing. That makes them a good comparison of engine overhead and a poor predictor of your application, where the database is usually the floor. Treat them as a reason to benchmark your own workload, and nothing more.

  • Page: a “Hello World” .cfm, served from a directory with an Application.cfc
  • RustCFML: --serve --production, current release
  • Lucee: version 7.0, warmed before the run; the QoQ table uses 7.0.4
  • Tool: Apache Bench, eight-second runs, median of five for the QoQ suite
  • Shipped binaries are built with profile-guided optimisation (fat LTO plus profile data); the cargo build --release you run yourself is not, and will be slower
  • Query-of-queries: the public cfml-qoq-perf-tests suite, 10 SELECTs over 1M in-memory rows

The QoQ figures were published against an earlier RustCFML (v0.112) and have not been restated since. The full write-up lives in docs/performance.md.

benchmark it yourself
# 1. start the engine the way you would ship it
rustcfml --serve ./webroot --production &

# 2. warm it, then measure
ab -n 2000 -c 10  http://127.0.0.1:8500/index.cfm
ab -n 5000 -c 50 -k http://127.0.0.1:8500/index.cfm

# 3. watch where the time actually goes
rustcfml --serve ./webroot --production --profile=30

# 4. compare a single template without the server at all
rustcfml ./webroot/index.cfm
Production mode

What --production actually changes

Development mode checks that files have not changed on every request so edits are picked up instantly. Production mode caches three lookups permanently (the Application.cfc resolution, URL-to-file resolution and compiled bytecode) and drops the per-request readdir. You reload by restarting. It is also honoured as RUSTCFML_PRODUCTION=1.

Detail in Deployment.

Tuning flags

Small switches with real effects

--single-threaded
Lower memory and lower concurrency, for a tiny instance
--socket
Bind a Unix socket beside your proxy; about 40% more throughput than TCP loopback at high concurrency
nginx + unix socket
upstream rustcfml {
    server unix:/run/rustcfml.sock;
    keepalive 32;
}

Benchmark it on your own workload

Grab the binary, point it at a real page and measure. If your numbers are worse than these, open an issue with the template. That is how the engine got faster.