Major optimizations to reduce allocations and improve execution speed:
1. For loops: Replace catch/throw with while + break flag
- Uses while loop with index instead of .each with catch/throw
- Break implemented with flag variable, continue with next
- Result: 18% fewer allocations, 85% faster for simple loops
2. Forloop property inlining
- Inline forloop.index as (__idx__ + 1), forloop.first as (__idx__ == 0), etc.
- Completely eliminates forloop hash allocation when all properties inlinable
- Result: Loop with forloop went from +46% MORE to -16% FEWER allocations
3. LR.to_array helper with EMPTY_ARRAY constant
- Centralized array conversion with frozen empty array for nil
- Avoids allocations for empty collections
4. Inline LR.truthy? calls
- Replace LR.truthy?(x) with (x != nil && x != false)
- Eliminates method call overhead in conditions
5. Keep Time methods available in sandbox for date filter
Overall results:
- Allocations: 3.5% MORE -> 24% FEWER (27% improvement)
- Time: 64% faster -> 89% faster (25% improvement)
Also adds:
- compile_profiler.rb for measuring allocations/performance
- compile_acceptance_test.rb for output equivalence testing
- OPTIMIZATION.md documenting optimization status
Add explicit summary showing pre-compiled is X times FASTER,
and explain that "X slower" in comparison means relative to
the fastest option (pre-compiled Ruby). Higher i/s = faster.
- Fix forloop.first/last/size lookups to try hash key before method call
- Fix tablerow cols parameter to use attributes['cols'] not @cols
- Fix tablerow output format to match interpreter (newlines, row boundaries)
- Fix capture compiler to access @body directly instead of iterating nodelist
- Add CompiledTemplate class to encapsulate code and external_tags
- Add external filter support via filter_handler
- Add debug mode warnings for external tag/filter calls
- Add Ruby 3.3 compatibility shim for peek_byte/scan_byte
- Add comprehensive unit tests for output equivalence
- Add benchmark comparing compiled vs interpreted rendering
All 30 test templates now produce identical output between compiled
Ruby and interpreted Liquid. Pre-compiled Ruby is 1.68x faster.
Psych 4 introduces a breaking change (ruby/psych#487) where
`Psych#load`/`Psych#load_file` now default to safe loading,
meaning that YAML references are not allowed anymore. This
commit changes the benchmark to use `Psych#unsafe_load_file`
when it's available.