11import { execSync } from 'node:child_process' ;
22import { Bench } from 'tinybench' ;
3- import Handlebars from '../lib/index.js' ;
3+ import Handlebars from '../../ lib/index.js' ;
44import { templates as allTemplates } from './templates.mjs' ;
55import {
66 printResults ,
@@ -9,8 +9,16 @@ import {
99} from './report.mjs' ;
1010
1111// ─── Configuration ───────────────────────────────────────────────────────────
12+ // Compilation happens once at app startup — low warmup reflects real-world conditions.
13+ // Execution happens thousands of times — higher warmup lets V8 optimize hot paths.
1214
13- const BENCH_CONFIG = {
15+ const COMPILE_BENCH_CONFIG = {
16+ warmupIterations : 10 ,
17+ iterations : 1000 ,
18+ time : 3000 ,
19+ } ;
20+
21+ const EXEC_BENCH_CONFIG = {
1422 warmupIterations : 500 ,
1523 iterations : 5000 ,
1624 time : 3000 ,
@@ -67,8 +75,8 @@ if (grepPattern) {
6775
6876// ─── Bench helpers ───────────────────────────────────────────────────────────
6977
70- function newBench ( ) {
71- return new Bench ( BENCH_CONFIG ) ;
78+ function newBench ( config ) {
79+ return new Bench ( config ) ;
7280}
7381
7482function createEnv ( def ) {
@@ -84,10 +92,10 @@ function createEnv(def) {
8492 return hb ;
8593}
8694
87- async function runSection ( title , setup ) {
95+ async function runSection ( title , config , setup ) {
8896 printSectionHeader ( title ) ;
8997
90- const bench = newBench ( ) ;
98+ const bench = newBench ( config ) ;
9199 setup ( bench ) ;
92100 await bench . run ( ) ;
93101 printResults ( bench ) ;
@@ -105,7 +113,7 @@ async function run() {
105113 console . log ( `Handlebars Performance Benchmark${ headerLabel } ` ) ;
106114 console . log ( '================================' ) ;
107115 console . log (
108- `tinybench | warmup: ${ BENCH_CONFIG . warmupIterations } | minIterations: ${ BENCH_CONFIG . iterations } | time: ${ BENCH_CONFIG . time } ms per bench`
116+ `tinybench | compile: warmup ${ COMPILE_BENCH_CONFIG . warmupIterations } , min ${ COMPILE_BENCH_CONFIG . iterations } | exec: warmup ${ EXEC_BENCH_CONFIG . warmupIterations } , min ${ EXEC_BENCH_CONFIG . iterations } | time: ${ EXEC_BENCH_CONFIG . time } ms per bench`
109117 ) ;
110118 console . log ( `Node ${ process . version } | ${ process . platform } ${ process . arch } ` ) ;
111119 console . log ( ) ;
@@ -115,30 +123,38 @@ async function run() {
115123 // ─── COMPILATION ────────────────────────────────────────────────────────────
116124
117125 allSections . push (
118- await runSection ( 'COMPILATION (Handlebars.compile)' , ( bench ) => {
119- for ( const [ name , def ] of Object . entries ( templates ) ) {
120- const hb = createEnv ( def ) ;
121- bench . add ( `compile: ${ name } ` , ( ) => {
122- hb . compile ( def . template ) ;
123- } ) ;
126+ await runSection (
127+ 'COMPILATION (Handlebars.precompile)' ,
128+ COMPILE_BENCH_CONFIG ,
129+ ( bench ) => {
130+ for ( const [ name , def ] of Object . entries ( templates ) ) {
131+ const hb = createEnv ( def ) ;
132+ bench . add ( `compile: ${ name } ` , ( ) => {
133+ hb . precompile ( def . template ) ;
134+ } ) ;
135+ }
124136 }
125- } )
137+ )
126138 ) ;
127139
128140 // ─── EXECUTION + output verification ────────────────────────────────────────
129141
130142 const expectedOutputs = { } ;
131143
132144 allSections . push (
133- await runSection ( 'EXECUTION (template rendering)' , ( bench ) => {
134- for ( const [ name , def ] of Object . entries ( templates ) ) {
135- const compiled = createEnv ( def ) . compile ( def . template ) ;
136- expectedOutputs [ name ] = compiled ( def . context ) ;
137- bench . add ( `exec: ${ name } ` , ( ) => {
138- compiled ( def . context ) ;
139- } ) ;
145+ await runSection (
146+ 'EXECUTION (template rendering)' ,
147+ EXEC_BENCH_CONFIG ,
148+ ( bench ) => {
149+ for ( const [ name , def ] of Object . entries ( templates ) ) {
150+ const compiled = createEnv ( def ) . compile ( def . template ) ;
151+ expectedOutputs [ name ] = compiled ( def . context ) ;
152+ bench . add ( `exec: ${ name } ` , ( ) => {
153+ compiled ( def . context ) ;
154+ } ) ;
155+ }
140156 }
141- } )
157+ )
142158 ) ;
143159
144160 // Verify outputs haven't changed during benchmarking
@@ -163,65 +179,78 @@ async function run() {
163179 // ─── PRECOMPILATION ─────────────────────────────────────────────────────────
164180
165181 allSections . push (
166- await runSection ( 'PRECOMPILATION (Handlebars.precompile)' , ( bench ) => {
167- for ( const [ name , def ] of Object . entries ( templates ) ) {
168- bench . add ( `precompile: ${ name } ` , ( ) => {
169- Handlebars . precompile ( def . template ) ;
170- } ) ;
182+ await runSection (
183+ 'PRECOMPILATION (Handlebars.precompile)' ,
184+ COMPILE_BENCH_CONFIG ,
185+ ( bench ) => {
186+ for ( const [ name , def ] of Object . entries ( templates ) ) {
187+ bench . add ( `precompile: ${ name } ` , ( ) => {
188+ Handlebars . precompile ( def . template ) ;
189+ } ) ;
190+ }
171191 }
172- } )
192+ )
173193 ) ;
174194
175195 // ─── END-TO-END ─────────────────────────────────────────────────────────────
176196
177197 allSections . push (
178- await runSection ( 'END-TO-END (compile + render)' , ( bench ) => {
179- for ( const [ name , def ] of Object . entries ( templates ) ) {
180- const hb = createEnv ( def ) ;
181- bench . add ( `e2e: ${ name } ` , ( ) => {
182- const fn = hb . compile ( def . template ) ;
183- fn ( def . context ) ;
184- } ) ;
198+ await runSection (
199+ 'END-TO-END (compile + render)' ,
200+ COMPILE_BENCH_CONFIG ,
201+ ( bench ) => {
202+ for ( const [ name , def ] of Object . entries ( templates ) ) {
203+ const hb = createEnv ( def ) ;
204+ bench . add ( `e2e: ${ name } ` , ( ) => {
205+ const fn = hb . compile ( def . template ) ;
206+ fn ( def . context ) ;
207+ } ) ;
208+ }
185209 }
186- } )
210+ )
187211 ) ;
188212
189213 // ─── COMPILE OPTIONS ───────────────────────────────────────────────────────
190214
191215 allSections . push (
192- await runSection ( 'COMPILE OPTIONS COMPARISON' , ( bench ) => {
193- const src = allTemplates [ 'complex (if/each/helpers)' ] . template ;
194- const ctx = allTemplates [ 'complex (if/each/helpers)' ] . context ;
195-
196- const defaultFn = Handlebars . compile ( src ) ;
197- bench . add ( 'exec: default options' , ( ) => defaultFn ( ctx ) ) ;
198-
199- const noEscapeFn = Handlebars . compile ( src , { noEscape : true } ) ;
200- bench . add ( 'exec: noEscape=true' , ( ) => noEscapeFn ( ctx ) ) ;
201-
202- const strictFn = Handlebars . compile ( src , {
203- strict : true ,
204- assumeObjects : true ,
205- } ) ;
206- bench . add ( 'exec: strict + assumeObjects' , ( ) => strictFn ( ctx ) ) ;
207-
208- const knownFn = Handlebars . compile ( src , {
209- knownHelpers : { if : true , each : true } ,
210- knownHelpersOnly : false ,
211- } ) ;
212- bench . add ( 'exec: knownHelpers' , ( ) => knownFn ( ctx ) ) ;
213-
214- const compatFn = Handlebars . compile ( src , { compat : true } ) ;
215- bench . add ( 'exec: compat=true' , ( ) => compatFn ( ctx ) ) ;
216-
217- const noDataFn = Handlebars . compile ( src , { data : false } ) ;
218- bench . add ( 'exec: data=false' , ( ) => noDataFn ( ctx ) ) ;
219- } )
216+ await runSection (
217+ 'COMPILE OPTIONS COMPARISON' ,
218+ EXEC_BENCH_CONFIG ,
219+ ( bench ) => {
220+ const src = allTemplates [ 'complex (if/each/helpers)' ] . template ;
221+ const ctx = allTemplates [ 'complex (if/each/helpers)' ] . context ;
222+
223+ const defaultFn = Handlebars . compile ( src ) ;
224+ bench . add ( 'exec: default options' , ( ) => defaultFn ( ctx ) ) ;
225+
226+ const noEscapeFn = Handlebars . compile ( src , { noEscape : true } ) ;
227+ bench . add ( 'exec: noEscape=true' , ( ) => noEscapeFn ( ctx ) ) ;
228+
229+ const strictFn = Handlebars . compile ( src , {
230+ strict : true ,
231+ assumeObjects : true ,
232+ } ) ;
233+ bench . add ( 'exec: strict + assumeObjects' , ( ) => strictFn ( ctx ) ) ;
234+
235+ const knownFn = Handlebars . compile ( src , {
236+ knownHelpers : { if : true , each : true } ,
237+ knownHelpersOnly : false ,
238+ } ) ;
239+ bench . add ( 'exec: knownHelpers' , ( ) => knownFn ( ctx ) ) ;
240+
241+ const compatFn = Handlebars . compile ( src , { compat : true } ) ;
242+ bench . add ( 'exec: compat=true' , ( ) => compatFn ( ctx ) ) ;
243+
244+ const noDataFn = Handlebars . compile ( src , { data : false } ) ;
245+ bench . add ( 'exec: data=false' , ( ) => noDataFn ( ctx ) ) ;
246+ }
247+ )
220248 ) ;
221249
222250 const filepath = saveMarkdownReport ( allSections , {
223251 label,
224- config : BENCH_CONFIG ,
252+ compileConfig : COMPILE_BENCH_CONFIG ,
253+ execConfig : EXEC_BENCH_CONFIG ,
225254 date : now ,
226255 } ) ;
227256 console . log ( `Results saved to: ${ filepath } ` ) ;
0 commit comments