CompiledFunction vs Plain Evaluation in Wolfram Language: A Decision Guide
Decide when to use Wolfram Language’s CompiledFunction for numeric loops versus plain evaluation. Compare performance, symbolic limits, deployment needs, and see a concrete timing example with sin(i)^2 summation.
24 Jan 2026, 22:16 UTC

When to Compile: The Decision Problem
In the Wolfram Language, code runs by default through the interpreter, which keeps full symbolic power and automatic type inference. For tight numeric loops that run millions of times, this overhead can dominate the runtime. CompiledFunction translates a subset of Wolfram Language into lower‑level bytecode or even C, eliminating interpreter overhead and often yielding 5–50× speed‑ups when the code is type‑stable and avoids high‑level symbolic constructs.
The core decision is whether the performance gain justifies the loss of symbolic flexibility and the extra deployment steps. Below is a concise decision guide.
Decision Factors
- Numeric Intensity: Is the function called >10⁵ times with simple arithmetic?
- Symbolic Needs: Does the code use pattern matching, Dynamic, Manipulate, or other high‑level features?
- Type Stability: Are all arguments numeric and of a single type (e.g., Integer, Real) throughout the function?
- Deployment Platform: Desktop, Server, Cloud, or embedded in another language?
- Compilation Time vs Runtime Gain: Is a minutes‑long compile acceptable for a one‑time startup?
- External Dependencies: Do you need to call C libraries or system calls?
Option Comparison Table
| Option | Performance | Symbolic Support | Deployment Complexity | Compilation Time | Typical Use‑Case |
|---|---|---|---|---|---|
| Plain Evaluation | Baseline (interpreted) | Full symbolic, pattern matching, Dynamic, etc. | None – runs immediately | None | Debugging, symbolic algebra, one‑off scripts |
| CompiledFunction (target "WVM") | 5–20× faster for tight loops | Limited – no Dynamic, Manipulate, or arbitrary symbolic calls | Requires Compile[] call; no external binaries | Seconds to minutes (depends on code size) | Numeric kernels, iterative solvers, statistical loops |
| CompiledFunction via LibraryLink (C) | 20–50× faster (CPU‑native) | None – the C code must be written in C, no Wolfram symbols | Build C library, distribute binary, load with LibraryFunctionLoad | Minutes to hours (C compile, library build) | High‑performance modules, cross‑language integration, deployment on cloud |
Trade‑Offs Explained
- Speed vs Flexibility: CompiledFunction removes the interpreter loop but also the ability to use symbolic features. If your algorithm needs symbolic simplification, stay with plain evaluation.
- Deployment Footprint: WVM compilation stays within the Wolfram ecosystem and works on any platform where the kernel runs. C libraries require matching architecture and may break across Wolfram versions.
- Debugging: Plain evaluation offers full traceability. Compiled functions obscure the call stack; use
CompiledFunctionTools`CompilePrintto inspect bytecode. - Compilation Overhead: For very small functions, the compile time may outweigh the runtime benefit. Profile first.
Concrete Implementation: Summing sin(i)^2 for 10⁷ Iterations
We’ll compare three variants:
- Plain evaluation using
Total[Sin[Range[n]]^2]. - CompiledFunction with target "WVM" that loops explicitly.
- CompiledFunction via LibraryLink that calls a minimal C function.
1. Plain Evaluation
// Run on the Wolfram kernel (requires administrative rights only for kernel launch)
ClearAll[plainSum]
plainSum[n_] := Total[Sin[Range[n]]^2]
AbsoluteTiming[plainSum[10^7]];
Expected output: a real number and a timing value. The interpreter processes each Sin call, the list building, and the Total reduction.
2. CompiledFunction (WVM)
// Compile a tight numeric loop
cfWVM = Compile[{{n, _Integer}},
Module[{sum = 0.0, i},
Do[sum += Sin[i]^2, {i, 1, n}];
sum],
CompilationTarget -> "WVM",
RuntimeAttributes -> {Listable}];
AbsoluteTiming[cfWVM[10^7]];
Key points:
- All arguments are typed as
Integer; the loop variableiis implicitly aRealbecauseSinreturnsReal. - We avoid
RangeandTotalto keep the code inside the compiler’s domain. - Check that the compiled function contains no
MainEvaluatecalls:CompiledFunctionTools`CompilePrint[cfWVM]
3. CompiledFunction via LibraryLink (C)
Write a minimal C wrapper that performs the same sum. The C code (sum.c) would look like:
#include <math.h>
#include <WolframLibrary.h>
DLLEXPORT int WolframLibrary_getVersion(int *major, int *minor) {
*major = 1; *minor = 0; return 0;
}
DLLEXPORT int SumSinSquares(WolframLibraryData libData, mint Argc, MArgument *Args, MArgument Res) {
mint n = MArgument_getInteger(Args[0]);
double sum = 0.0;
for (mint i = 1; i <= n; ++i) sum += sin((double)i) * sin((double)i);
MArgument_setReal(Res, sum);
return 0;
}
Compile the library:
CreateLibrary[{"sum.c", "sum.o"}, "sumLib", {
"Library" -> "sumLib",
"LibraryDirectory" -> "./lib",
"CompilerOptions" -> {"-O3"}
}]
Load and call from Mathematica:
lib = LibraryFunctionLoad["./lib/sumLib", {Integer}, Real];
AbsoluteTiming[lib[10^7]];
Verify correctness by comparing a small test set:
plainSum[100] == lib[100] // N
Verification Checklist
- Timing: Run
AbsoluteTimingfor each variant on the same machine. Expect ~5–10× speed‑up for WVM and ~20–30× for C. - Correctness: For a random
n(e.g., 12345), compare the results from all three functions to within machine epsilon. - Compilation Output: Use
CompiledFunctionTools`CompilePrintto ensure noMainEvaluateremains in the WVM target. - Library Load: If using LibraryLink, confirm that
lib[10^7]returns aRealand that the library file matches the kernel’s architecture.
Practical Takeaway
Choose plain evaluation when:
- The code is heavily symbolic or uses pattern matching.
- The function is called only a few times.
- Rapid prototyping or debugging is required.
Choose CompiledFunction (WVM) when:
- You need a 5–20× speed‑up for a tight numeric loop.
- The code is type‑stable and free of high‑level Wolfram constructs.
- Deployment must remain cross‑platform without external binaries.
Choose CompiledFunction via LibraryLink (C) when:
- Maximum performance is critical (e.g., >10⁸ iterations).
- You can provide a C compiler and manage platform‑specific binaries.
- Integration with other languages or systems is required.
Always profile your specific workload; the numbers above are typical but can vary with kernel version, hardware, and code complexity. Use the verification steps above to ensure your decision delivers the expected benefit without introducing bugs.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.