1+ const romans = require ( './romans' )
2+ const { deromanize, romanize } = require ( './romans' )
3+
4+ // Benchmark configuration
5+ const ITERATIONS = {
6+ WARMUP : 1000 ,
7+ SMALL : 10000 ,
8+ MEDIUM : 100000 ,
9+ LARGE : 1000000
10+ }
11+
12+ // Common test values
13+ const TEST_VALUES = {
14+ SIMPLE : [ 1 , 5 , 10 , 50 , 100 , 500 , 1000 ] ,
15+ COMPLEX : [ 444 , 999 , 1994 , 2023 , 3999 ] ,
16+ RANDOM : Array . from ( { length : 100 } , ( ) => Math . floor ( Math . random ( ) * 3999 ) + 1 )
17+ }
18+
19+ const TEST_ROMANS = {
20+ SIMPLE : [ 'I' , 'V' , 'X' , 'L' , 'C' , 'D' , 'M' ] ,
21+ COMPLEX : [ 'CDXLIV' , 'CMXCIX' , 'MCMXCIV' , 'MMXXIII' , 'MMMCMXCIX' ] ,
22+ RANDOM : TEST_VALUES . RANDOM . map ( n => romanize ( n ) )
23+ }
24+
25+ function benchmark ( name , fn , iterations = ITERATIONS . MEDIUM ) {
26+ // Warmup
27+ for ( let i = 0 ; i < ITERATIONS . WARMUP ; i ++ ) {
28+ fn ( )
29+ }
30+
31+ const start = process . hrtime . bigint ( )
32+ for ( let i = 0 ; i < iterations ; i ++ ) {
33+ fn ( )
34+ }
35+ const end = process . hrtime . bigint ( )
36+
37+ const durationMs = Number ( end - start ) / 1000000
38+ const opsPerSec = ( iterations / durationMs ) * 1000
39+
40+ console . log ( `${ name } :` )
41+ console . log ( ` ${ iterations . toLocaleString ( ) } ops in ${ durationMs . toFixed ( 2 ) } ms` )
42+ console . log ( ` ${ opsPerSec . toLocaleString ( ) } ops/sec` )
43+ console . log ( ` ${ ( durationMs / iterations * 1000 ) . toFixed ( 3 ) } μs per op` )
44+ console . log ( )
45+
46+ return { duration : durationMs , opsPerSec }
47+ }
48+
49+ function benchmarkRomanize ( ) {
50+ console . log ( '=== ROMANIZE BENCHMARKS ===\n' )
51+
52+ // Simple numbers
53+ benchmark ( 'Simple numbers (1-1000)' , ( ) => {
54+ TEST_VALUES . SIMPLE . forEach ( n => romanize ( n ) )
55+ } )
56+
57+ // Complex numbers
58+ benchmark ( 'Complex numbers' , ( ) => {
59+ TEST_VALUES . COMPLEX . forEach ( n => romanize ( n ) )
60+ } )
61+
62+ // Random numbers
63+ benchmark ( 'Random numbers' , ( ) => {
64+ TEST_VALUES . RANDOM . forEach ( n => romanize ( n ) )
65+ } )
66+
67+ // Sequential 1-3999
68+ benchmark ( 'Sequential 1-3999' , ( ) => {
69+ for ( let i = 1 ; i < 4000 ; i ++ ) {
70+ romanize ( i )
71+ }
72+ } , 100 )
73+
74+ // Single value repeated
75+ benchmark ( 'Single value (1994) repeated' , ( ) => {
76+ romanize ( 1994 )
77+ } )
78+ }
79+
80+ function benchmarkDeromanize ( ) {
81+ console . log ( '=== DEROMANIZE BENCHMARKS ===\n' )
82+
83+ // Simple romans
84+ benchmark ( 'Simple romans' , ( ) => {
85+ TEST_ROMANS . SIMPLE . forEach ( r => deromanize ( r ) )
86+ } )
87+
88+ // Complex romans
89+ benchmark ( 'Complex romans' , ( ) => {
90+ TEST_ROMANS . COMPLEX . forEach ( r => deromanize ( r ) )
91+ } )
92+
93+ // Random romans
94+ benchmark ( 'Random romans' , ( ) => {
95+ TEST_ROMANS . RANDOM . forEach ( r => deromanize ( r ) )
96+ } )
97+
98+ // Single value repeated
99+ benchmark ( 'Single value (MCMXCIV) repeated' , ( ) => {
100+ deromanize ( 'MCMXCIV' )
101+ } )
102+ }
103+
104+ function benchmarkRoundTrip ( ) {
105+ console . log ( '=== ROUND-TRIP BENCHMARKS ===\n' )
106+
107+ benchmark ( 'Round-trip conversion' , ( ) => {
108+ TEST_VALUES . RANDOM . forEach ( n => {
109+ const roman = romanize ( n )
110+ deromanize ( roman )
111+ } )
112+ } )
113+
114+ benchmark ( 'Sequential round-trip 1-1000' , ( ) => {
115+ for ( let i = 1 ; i <= 1000 ; i ++ ) {
116+ const roman = romanize ( i )
117+ deromanize ( roman )
118+ }
119+ } , 1000 )
120+ }
121+
122+ function memoryBenchmark ( ) {
123+ console . log ( '=== MEMORY BENCHMARKS ===\n' )
124+
125+ const before = process . memoryUsage ( )
126+
127+ // Create lots of conversions
128+ const results = [ ]
129+ for ( let i = 0 ; i < 100000 ; i ++ ) {
130+ const num = Math . floor ( Math . random ( ) * 3999 ) + 1
131+ results . push ( romanize ( num ) )
132+ }
133+
134+ for ( let i = 0 ; i < results . length ; i ++ ) {
135+ deromanize ( results [ i ] )
136+ }
137+
138+ const after = process . memoryUsage ( )
139+
140+ console . log ( 'Memory usage:' )
141+ console . log ( ` Heap Used: ${ ( ( after . heapUsed - before . heapUsed ) / 1024 / 1024 ) . toFixed ( 2 ) } MB` )
142+ console . log ( ` Heap Total: ${ ( ( after . heapTotal - before . heapTotal ) / 1024 / 1024 ) . toFixed ( 2 ) } MB` )
143+ console . log ( ` External: ${ ( ( after . external - before . external ) / 1024 / 1024 ) . toFixed ( 2 ) } MB` )
144+ console . log ( )
145+ }
146+
147+ function profileSpecificCases ( ) {
148+ console . log ( '=== SPECIFIC CASE PROFILES ===\n' )
149+
150+ // Worst case scenarios
151+ const worstCases = {
152+ 'Maximum value (3999)' : ( ) => romanize ( 3999 ) ,
153+ 'Complex subtractive (444)' : ( ) => romanize ( 444 ) ,
154+ 'Many Ms (3000)' : ( ) => romanize ( 3000 ) ,
155+ 'Long roman (MMMCMXCIX)' : ( ) => deromanize ( 'MMMCMXCIX' ) ,
156+ 'Complex roman (CDXLIV)' : ( ) => deromanize ( 'CDXLIV' )
157+ }
158+
159+ Object . entries ( worstCases ) . forEach ( ( [ name , fn ] ) => {
160+ benchmark ( name , fn , ITERATIONS . LARGE )
161+ } )
162+ }
163+
164+ function main ( ) {
165+ console . log ( 'Romans Library Performance Benchmark' )
166+ console . log ( '====================================\n' )
167+
168+ console . log ( `Node.js ${ process . version } ` )
169+ console . log ( `Platform: ${ process . platform } ${ process . arch } ` )
170+ console . log ( `Memory: ${ Math . round ( process . memoryUsage ( ) . heapTotal / 1024 / 1024 ) } MB\n` )
171+
172+ benchmarkRomanize ( )
173+ benchmarkDeromanize ( )
174+ benchmarkRoundTrip ( )
175+ profileSpecificCases ( )
176+ memoryBenchmark ( )
177+
178+ console . log ( 'Benchmark complete!' )
179+ }
180+
181+ if ( require . main === module ) {
182+ main ( )
183+ }
184+
185+ module . exports = {
186+ benchmark,
187+ benchmarkRomanize,
188+ benchmarkDeromanize,
189+ benchmarkRoundTrip,
190+ memoryBenchmark,
191+ profileSpecificCases
192+ }
0 commit comments