Skip to content

Commit 8a144af

Browse files
committed
feat(template): V8 compiled expressions and true execution plan runtime
- compile expressions into instructions (VariableInstr, JumpIfFalseInstr) - remove runtime expression parsing (no Lexer/Parser during render) - add deep cloning of expression AST in compiler - use direct expression evaluation in renderer - optimize render_template_by_name() fast path (no RenderResult allocations) - improve execution plan efficiency and CPU locality Performance: - variable: ~2.5x faster - expression: ~4x faster - if: ~4x faster - loops: ~2x faster - mixed templates: ~2x faster - cached templates: ~3–4x faster Rendering now exceeds V6 performance on core paths. Note: - include/composition remains the main bottleneck (next focus)
1 parent 1fb15c1 commit 8a144af

7 files changed

Lines changed: 297 additions & 260 deletions

File tree

CHANGELOG.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,79 @@
11
# Changelog
22

3+
## v0.8.0
4+
5+
Make rendering truly compiled by eliminating runtime expression parsing.
6+
7+
### Highlights
8+
9+
V8 turns the execution plan into a real compiled runtime by removing expression reparsing during rendering.
10+
11+
Expressions are now compiled once and evaluated directly, unlocking major performance gains across the entire rendering pipeline.
12+
13+
### Features
14+
15+
- Compile expressions into instructions
16+
- `VariableInstr` now stores compiled expressions
17+
- `JumpIfFalseInstr` uses compiled conditions
18+
- Remove runtime expression parsing
19+
- no more Lexer/Parser during render
20+
- direct evaluation from execution plan
21+
- Improve execution plan efficiency
22+
- fully compiled rendering path
23+
- better CPU cache locality
24+
25+
### Performance Improvements
26+
27+
- Variable rendering: ~2.5x faster
28+
- Expression rendering: ~4x faster
29+
- Conditional rendering (`if`): ~4x faster
30+
- Loops: ~2x faster
31+
- Mixed templates: ~2x faster
32+
- Engine cached templates: ~3–4x faster
33+
34+
Rendering performance now exceeds V6 across most scenarios.
35+
36+
### Renderer Improvements
37+
38+
- Remove `evaluate_compiled_expression(std::string, ...)`
39+
- Use `evaluate_expression(Expression, ...)` directly
40+
- Avoid temporary parsing structures during execution
41+
- Optimize `render_template_by_name()`:
42+
- reduce allocations
43+
- avoid unnecessary `RenderResult` copies
44+
- use `execute_plan()` directly on fast path
45+
46+
### Compiler Improvements
47+
48+
- Add deep cloning of expression AST
49+
- Store compiled expressions inside execution plan
50+
- Remove `expression_to_string()` path
51+
52+
### Performance Notes
53+
54+
- Core rendering path is now significantly faster than V6
55+
- Include and template composition still remain the main bottleneck
56+
- Cache lookup remains sub-microsecond
57+
58+
### Why this release matters
59+
60+
V6 introduced execution plans.
61+
V7 introduced correctness (cache + inheritance).
62+
63+
V8 completes the model:
64+
65+
- no runtime parsing
66+
- true compiled execution
67+
- predictable performance
68+
69+
This version marks the transition from a partially compiled engine to a fully compiled template runtime.
70+
71+
### Next Focus
72+
73+
- Optimize include and template composition
74+
- Reduce loader and cache overhead
75+
- Further improve inheritance execution path
76+
377
## v0.7.0
478

579
Add cache-aware template freshness and safer inheritance rendering.

include/vix/template/Compiler.hpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
#include <vix/template/AST.hpp>
2323
#include <vix/template/ExecutionPlan.hpp>
24+
#include <vix/template/Instruction.hpp>
2425
#include <vix/template/Loader.hpp>
2526
#include <vix/template/Optimizer.hpp>
2627
#include <vix/template/Template.hpp>
@@ -48,6 +49,9 @@ namespace vix::template_
4849
* - faster rendering
4950
* - better CPU cache locality
5051
* - compiled template caching in V7
52+
*
53+
* In V8, expressions stored in instructions are kept in compiled form
54+
* so the renderer can evaluate them directly without reparsing text.
5155
*/
5256
class Compiler
5357
{
@@ -161,6 +165,30 @@ namespace vix::template_
161165
const BlockNode &node,
162166
ExecutionPlan &plan) const;
163167

168+
/**
169+
* @brief Clone an expression tree into a shared compiled form.
170+
*
171+
* Instructions store shared compiled expressions so they remain
172+
* copyable and cheap to move while avoiding expression reparsing
173+
* during rendering.
174+
*
175+
* @param expression Expression AST to clone.
176+
* @return Shared pointer to an immutable compiled expression tree.
177+
*/
178+
[[nodiscard]] CompiledExprPtr clone_expression(
179+
const Expression &expression) const;
180+
181+
/**
182+
* @brief Clone an expression tree into a unique expression node.
183+
*
184+
* This helper performs the recursive deep copy of the expression AST.
185+
*
186+
* @param expression Expression AST to clone.
187+
* @return Deep-copied unique expression node.
188+
*/
189+
[[nodiscard]] ExprPtr clone_expression_node(
190+
const Expression &expression) const;
191+
164192
private:
165193
/**
166194
* @brief AST optimizer used before execution plan generation.

include/vix/template/Instruction.hpp

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,28 @@
1616
#ifndef VIX_TEMPLATE_INSTRUCTION_HPP
1717
#define VIX_TEMPLATE_INSTRUCTION_HPP
1818

19+
#include <cstdint>
20+
#include <memory>
1921
#include <string>
22+
#include <utility>
2023
#include <variant>
21-
#include <cstdint>
2224
#include <vector>
25+
2326
#include <vix/template/AST.hpp>
2427

2528
namespace vix::template_
2629
{
30+
/**
31+
* @brief Shared compiled expression pointer used by the execution plan.
32+
*
33+
* The execution plan must keep expressions in compiled form so the renderer
34+
* can evaluate them directly at runtime without reparsing expression text.
35+
*
36+
* Shared ownership is used here to keep Instruction copyable and movable
37+
* while still storing precompiled expression trees.
38+
*/
39+
using CompiledExprPtr = std::shared_ptr<const Expression>;
40+
2741
/**
2842
* @brief Enumeration of all instruction types used by the execution plan.
2943
*
@@ -80,27 +94,41 @@ namespace vix::template_
8094
*/
8195
struct TextInstr
8296
{
97+
/**
98+
* @brief Literal text emitted directly to output.
99+
*/
83100
std::string text;
84101
};
85102

86103
/**
87104
* @brief Payload for EmitVariable instruction.
88105
*
89-
* Stores the variable name to resolve at runtime.
106+
* Stores the already compiled expression to evaluate at runtime.
107+
* This avoids reparsing the variable expression during rendering.
90108
*/
91109
struct VariableInstr
92110
{
93-
std::string expression;
111+
/**
112+
* @brief Precompiled expression to evaluate.
113+
*/
114+
CompiledExprPtr expression;
115+
116+
/**
117+
* @brief Filter pipeline applied after expression evaluation.
118+
*/
94119
std::vector<FilterNode> filters;
95120
};
96121

97122
/**
98123
* @brief Payload for jump instructions.
99124
*
100-
* Offset represents the index to jump to in the instruction list.
125+
* Target represents the index to jump to in the instruction list.
101126
*/
102127
struct JumpInstr
103128
{
129+
/**
130+
* @brief Destination instruction index.
131+
*/
104132
std::size_t target{0};
105133
};
106134

@@ -111,20 +139,38 @@ namespace vix::template_
111139
*/
112140
struct ForEachInstr
113141
{
142+
/**
143+
* @brief Iterable variable name resolved at runtime.
144+
*/
114145
std::string iterable_name;
146+
147+
/**
148+
* @brief Loop item variable name bound for each iteration.
149+
*/
115150
std::string item_name;
151+
152+
/**
153+
* @brief Instruction index to jump to when the loop ends.
154+
*/
116155
std::size_t jump_end{0};
117156
};
118157

119158
/**
120159
* @brief Payload for conditional jump instructions.
121160
*
122-
* The expression is evaluated at runtime.
123-
* If the result is falsy, execution jumps to target.
161+
* The condition is stored in compiled form and evaluated directly
162+
* at runtime. If the result is falsy, execution jumps to target.
124163
*/
125164
struct JumpIfFalseInstr
126165
{
127-
std::string expression;
166+
/**
167+
* @brief Precompiled condition expression.
168+
*/
169+
CompiledExprPtr expression;
170+
171+
/**
172+
* @brief Destination instruction index when condition is false.
173+
*/
128174
std::size_t target{0};
129175
};
130176

@@ -133,6 +179,9 @@ namespace vix::template_
133179
*/
134180
struct IncludeInstr
135181
{
182+
/**
183+
* @brief Logical template name to include.
184+
*/
136185
std::string template_name;
137186
};
138187

include/vix/template/Renderer.hpp

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <vix/template/Cache.hpp>
2828
#include <vix/template/Context.hpp>
2929
#include <vix/template/ExecutionPlan.hpp>
30+
#include <vix/template/Instruction.hpp>
3031
#include <vix/template/Loader.hpp>
3132
#include <vix/template/RenderResult.hpp>
3233
#include <vix/template/Value.hpp>
@@ -47,13 +48,17 @@ namespace vix::template_
4748
* - cache locality
4849
*
4950
* The AST is still used for:
50-
* - expression evaluation
51+
* - direct evaluation of compiled expressions
5152
* - include / extends support
5253
* - block inheritance
5354
* - future advanced compilation features
5455
*
5556
* In V7, Renderer can optionally use a compiled template cache in order
5657
* to avoid re-loading and re-compiling included templates repeatedly.
58+
*
59+
* In V8, execution-plan instructions carry compiled expressions directly,
60+
* allowing the renderer to evaluate them without reparsing expression text
61+
* during rendering.
5762
*/
5863
class Renderer
5964
{
@@ -169,6 +174,9 @@ namespace vix::template_
169174
/**
170175
* @brief Execute a variable emission instruction.
171176
*
177+
* The instruction already carries a compiled expression, which is
178+
* evaluated directly at runtime.
179+
*
172180
* @param instr Variable instruction payload.
173181
* @param context Runtime rendering context.
174182
* @param output Output string buffer.
@@ -181,6 +189,8 @@ namespace vix::template_
181189
/**
182190
* @brief Execute a conditional jump instruction.
183191
*
192+
* The condition expression is already compiled in the instruction.
193+
*
184194
* @param instr Conditional jump payload.
185195
* @param instruction_index Mutable instruction pointer.
186196
* @param context Runtime rendering context.
@@ -234,20 +244,12 @@ namespace vix::template_
234244
const Context &context,
235245
std::string &output) const;
236246

237-
/**
238-
* @brief Evaluate an expression represented as text.
239-
*
240-
* @param expression_text Canonical expression string.
241-
* @param context Runtime rendering context.
242-
* @return Evaluated value.
243-
*/
244-
[[nodiscard]] Value evaluate_compiled_expression(
245-
const std::string &expression_text,
246-
const Context &context) const;
247-
248247
/**
249248
* @brief Evaluate an expression AST node.
250249
*
250+
* This is the primary evaluation path for expressions stored in the
251+
* execution plan.
252+
*
251253
* @param expr Expression to evaluate.
252254
* @param context Runtime rendering context.
253255
* @return Evaluated value.

0 commit comments

Comments
 (0)