The use "file.lk" that quietly cost us 57x
The use "file.lk" that quietly cost us 57×
We were building a real app in LOOK — a QR-menu ordering demo — and vendoring a few
packages the honest way: use "lib/qr.lk", use "lib/pdf.lk", and so on. The app worked.
It was just… slow. Not "needs a database index" slow — ~554 ms per request on a route
that should have been single-digit milliseconds.
Nothing in the code explained it. So we measured instead of guessing, and the number pointed
straight at the engine: the compute route ran at ~554 ms on the tree-walk interpreter and ~9 ms
on the bytecode VM. The whole app was silently running on the interpreter — ~57× slower.
The cause was a gap we'd never noticed because nothing errored: the bytecode compiler handled
module use, but a file-include (use "lib/qr.lk") fell back to the interpreter for the
entire program. And the vendored packages — the QR matrix, PDF rendering, inflate — were exactly
the compute-heavy code paying that tax. A user vendoring packages would lose the VM and have no
idea why.
The fix taught the compiler to compile file-includes into the VM, mirroring the module path. The
same route dropped from ~554 ms to ~8 ms, and the app now runs with **zero interpreter
fallback**. Differential tests (tree-walk == CLI-VM == web-VM) stayed green.
The lesson we keep relearning: when something is mysteriously slow, don't theorize — profile.
The bottleneck was never in the app. It was one silent fallback in the engine, and a real app
built on real packages was the only thing that could surface it.