romans
Version:
A small, no-dependency lib for converting to and from roman numerals
192 lines (155 loc) • 4.89 kB
JavaScript
const romans = require('./romans')
const { deromanize, romanize } = require('./romans')
// Benchmark configuration
const ITERATIONS = {
WARMUP: 1000,
SMALL: 10000,
MEDIUM: 100000,
LARGE: 1000000
}
// Common test values
const TEST_VALUES = {
SIMPLE: [1, 5, 10, 50, 100, 500, 1000],
COMPLEX: [444, 999, 1994, 2023, 3999],
RANDOM: Array.from({ length: 100 }, () => Math.floor(Math.random() * 3999) + 1)
}
const TEST_ROMANS = {
SIMPLE: ['I', 'V', 'X', 'L', 'C', 'D', 'M'],
COMPLEX: ['CDXLIV', 'CMXCIX', 'MCMXCIV', 'MMXXIII', 'MMMCMXCIX'],
RANDOM: TEST_VALUES.RANDOM.map(n => romanize(n))
}
function benchmark(name, fn, iterations = ITERATIONS.MEDIUM) {
// Warmup
for (let i = 0; i < ITERATIONS.WARMUP; i++) {
fn()
}
const start = process.hrtime.bigint()
for (let i = 0; i < iterations; i++) {
fn()
}
const end = process.hrtime.bigint()
const durationMs = Number(end - start) / 1000000
const opsPerSec = (iterations / durationMs) * 1000
console.log(`${name}:`)
console.log(` ${iterations.toLocaleString()} ops in ${durationMs.toFixed(2)}ms`)
console.log(` ${opsPerSec.toLocaleString()} ops/sec`)
console.log(` ${(durationMs / iterations * 1000).toFixed(3)}μs per op`)
console.log()
return { duration: durationMs, opsPerSec }
}
function benchmarkRomanize() {
console.log('=== ROMANIZE BENCHMARKS ===\n')
// Simple numbers
benchmark('Simple numbers (1-1000)', () => {
TEST_VALUES.SIMPLE.forEach(n => romanize(n))
})
// Complex numbers
benchmark('Complex numbers', () => {
TEST_VALUES.COMPLEX.forEach(n => romanize(n))
})
// Random numbers
benchmark('Random numbers', () => {
TEST_VALUES.RANDOM.forEach(n => romanize(n))
})
// Sequential 1-3999
benchmark('Sequential 1-3999', () => {
for (let i = 1; i < 4000; i++) {
romanize(i)
}
}, 100)
// Single value repeated
benchmark('Single value (1994) repeated', () => {
romanize(1994)
})
}
function benchmarkDeromanize() {
console.log('=== DEROMANIZE BENCHMARKS ===\n')
// Simple romans
benchmark('Simple romans', () => {
TEST_ROMANS.SIMPLE.forEach(r => deromanize(r))
})
// Complex romans
benchmark('Complex romans', () => {
TEST_ROMANS.COMPLEX.forEach(r => deromanize(r))
})
// Random romans
benchmark('Random romans', () => {
TEST_ROMANS.RANDOM.forEach(r => deromanize(r))
})
// Single value repeated
benchmark('Single value (MCMXCIV) repeated', () => {
deromanize('MCMXCIV')
})
}
function benchmarkRoundTrip() {
console.log('=== ROUND-TRIP BENCHMARKS ===\n')
benchmark('Round-trip conversion', () => {
TEST_VALUES.RANDOM.forEach(n => {
const roman = romanize(n)
deromanize(roman)
})
})
benchmark('Sequential round-trip 1-1000', () => {
for (let i = 1; i <= 1000; i++) {
const roman = romanize(i)
deromanize(roman)
}
}, 1000)
}
function memoryBenchmark() {
console.log('=== MEMORY BENCHMARKS ===\n')
const before = process.memoryUsage()
// Create lots of conversions
const results = []
for (let i = 0; i < 100000; i++) {
const num = Math.floor(Math.random() * 3999) + 1
results.push(romanize(num))
}
for (let i = 0; i < results.length; i++) {
deromanize(results[i])
}
const after = process.memoryUsage()
console.log('Memory usage:')
console.log(` Heap Used: ${((after.heapUsed - before.heapUsed) / 1024 / 1024).toFixed(2)} MB`)
console.log(` Heap Total: ${((after.heapTotal - before.heapTotal) / 1024 / 1024).toFixed(2)} MB`)
console.log(` External: ${((after.external - before.external) / 1024 / 1024).toFixed(2)} MB`)
console.log()
}
function profileSpecificCases() {
console.log('=== SPECIFIC CASE PROFILES ===\n')
// Worst case scenarios
const worstCases = {
'Maximum value (3999)': () => romanize(3999),
'Complex subtractive (444)': () => romanize(444),
'Many Ms (3000)': () => romanize(3000),
'Long roman (MMMCMXCIX)': () => deromanize('MMMCMXCIX'),
'Complex roman (CDXLIV)': () => deromanize('CDXLIV')
}
Object.entries(worstCases).forEach(([name, fn]) => {
benchmark(name, fn, ITERATIONS.LARGE)
})
}
function main() {
console.log('Romans Library Performance Benchmark')
console.log('====================================\n')
console.log(`Node.js ${process.version}`)
console.log(`Platform: ${process.platform} ${process.arch}`)
console.log(`Memory: ${Math.round(process.memoryUsage().heapTotal / 1024 / 1024)} MB\n`)
benchmarkRomanize()
benchmarkDeromanize()
benchmarkRoundTrip()
profileSpecificCases()
memoryBenchmark()
console.log('Benchmark complete!')
}
if (require.main === module) {
main()
}
module.exports = {
benchmark,
benchmarkRomanize,
benchmarkDeromanize,
benchmarkRoundTrip,
memoryBenchmark,
profileSpecificCases
}