Reference

The accelerator

The parser compiles a formula to byte-code once and interprets it after that. The accelerator goes one step further: it turns the byte-code into x86-64 machine code and calls it directly. The API does not change - you construct TJitParser instead of TMathParser and carry on.

The rule it never breaks: anything it cannot compile falls to the stage below it - the intermediate one, and the interpreter behind that. That fall back is not a setting: there is no way to switch it off, because fast but wrong is not a trade this library makes.

What it compiles

KindCovered
Arithmetic+ - × ÷, signs, nested brackets, constants
Comparisonall six, with the epsilon the parser is configured with
Branchingif, while, repeat - lazily, as the interpreter does
Variablesboth kinds: bound by typed reference, and boxed in a TValue
Scopeget and set on script variables
Callssin cos tan sqrt sqr ln exp abs arctan

What it declines

Each of these hands the formula down a stage, whole: to the intermediate one, and to the interpreter if that declines it too. The reason is available as CodeReason if you want to know why a particular formula did not compile.

DeclinesBecause
A call it has no machine version ofanything outside the nine above
A function with parametersmean, poly and the rest of the variadic set
A string constantthe code generator works in Double only
An integer beyond exact rangeabove 253 a Double stops counting by ones
A variable it cannot typea type with no machine representation
A frame deeper than 4 KBa formula with hundreds of live intermediates

What it costs, in nanoseconds

Every row compares the same work done twice, with the accelerator and without it. What one row measures is not what the next one does, so it is worth saying plainly: the first three are a single evaluation of a formula that is already parsed - the interpreter walking the script against machine code running it; the loop row is one iteration inside a script that loops ten thousand times; the bulk rows are one input out of an array handed over in a single call, against the same inputs fed one AsDouble at a time.

Measured on Delphi 13, Windows, x86-64. The number of runs each row was averaged over is in the table, because it is not the same for all of them. The programs that produce these numbers ship with the repository - run them on your own machine rather than trusting the table.

FormulaInterpretedCompiledTimes fasterRuns
x * 2 + 1142.211.612×2 000 000
polynomial, degree 3620.020.830×1 000 000
heavy math chain967.0106.5500 000
loop, 10 000 iterations3969.235.5112×200 000
bulk mode, x * 2 + 1893.07.7116×500 000
bulk mode, polynomial1968.611.8167×500 000

Bulk mode hands the accelerator an array of inputs and gets an array of answers, so the call overhead is paid once instead of a million times.

How it is kept honest

A generator writes random formulas - nested brackets, mixed precedence, branches, loops - and both engines evaluate each one. The run that ships with the repository covers 3 000 formulas with zero disagreements, and the compiler declined none of them. When it does decline something it says why in one word, and the stage below answers instead.

One number will differ, and here is why

The interpreter keeps intermediate values in Extended; the accelerator works in Double. On a build where Extended is wider than 64 bits - 32-bit Delphi, and FPC on Linux - the last bit of a long chain can differ between the two.

The fuzzer compares within an epsilon, so it passes; a bit-exact comparison would not. If your work needs the two engines to agree bit for bit, stay on the interpreter. See Limitations.

What it needs

RequirementDetail
Processorx86-64 only - there is no ARM or 32-bit code generator
Operating systemWindows and Linux; memory is taken with VirtualAlloc or mmap as appropriate
Memory protectionthe page is writable while the code is emitted, then read and execute - never both at once, so W^X is respected
Fallbackon any other target the machine-code stage is skipped and the intermediate one answers, with the interpreter behind it - nothing breaks