console.clear() function splitStringAtSeparator(str, separator) { const separatorIndex = str.indexOf(separator) if (separatorIndex === -1) { return [str, ''] } else { const beforeSeparator = str.slice(0, separatorIndex) const afterSeparator = str.slice(separatorIndex + separator.length) return [beforeSeparator, afterSeparator] } } const REGISTRY = {} const compileNewVector = (length, params, componentFn) => { const label = `vector<${length}>(${componentFn.toString()})` // console.log(`requested function for "${label}"`) let fn = REGISTRY[label] if (!fn) { const [varsRaw, codeRaw] = splitStringAtSeparator(componentFn.toString(), '=>') const freeIndex = varsRaw.replace(/[()]/g, '').trim() const code = codeRaw.trim() // console.log('compiling...') let source = `const out = Array(${length});\n` for (let i = 0; i < length; i++) { source += `out[${i}] = ${code.replaceAll(freeIndex, i)};\n` } source += `return out;` // console.log(`=> Source: "${source.replaceAll('\n', ' ')}"`) fn = new Function(...Object.keys(params), source) REGISTRY[label] = fn } else { // console.log('using cached') } return fn(...Object.values(params)) } const compileSum = (length, params, componentFn) => { const label = `sum<${length}>(${componentFn.toString()})` // console.log(`requested function for "${label}"`) let fn = REGISTRY[label] if (!fn) { const [varsRaw, codeRaw] = splitStringAtSeparator(componentFn.toString(), '=>') const freeIndex = varsRaw.replace(/[()]/g, '').trim() const code = codeRaw.trim() // console.log('compiling...') let source = `let out = 0;\n` for (let i = 0; i < length; i++) { source += `out += ${code.replaceAll(freeIndex, i)};\n` } source += `return out;` // console.log(`=> Source: "${source.replaceAll('\n', ' ')}"`) fn = new Function(...Object.keys(params), source) REGISTRY[label] = fn } else { // console.log('using cached') } return fn(...Object.values(params)) } const operators = { ['+'](v, w) { return compileNewVector(v.length, { v, w }, i => v[i] + w[i]) }, ['*'](v, w) { return compileNewVector(v.length, { v, w }, i => v[i] * w[i]) }, ['-'](v, w) { return compileNewVector(v.length, { v, w }, i => v[i] - w[i]) }, ['/'](v, w) { return compileNewVector(v.length, { v, w }, i => v[i] / w[i]) }, ['dot'](v, w) { const vw = operators['*'](v, w) return compileSum(v.length, { vw }, i => vw[i]) }, } export const m = (fragments, ...args) => { const opName = fragments[1].trim() return operators[opName](...args) } const v1 = [1, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3] const w1 = [2, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4] const v2 = [1, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3] const w2 = [2, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4] function dotVectors(v, w) { return v.map((vi, i) => vi * w[i]).reduce((acc, vw) => acc + vw, 0) } // Benchmarking code const iterations = 50000 for (let i = 0; i < 10; i++) { const start = performance.now() let result for (let i = 0; i < iterations; i++) { result = i % 2 === 0 ? m`${v1} dot ${w1}` : m`${v2} dot ${w2}` } const end = performance.now() console.log(`Time for ${iterations} iterations: ${end - start}ms`) } console.log('----------') for (let i = 0; i < 10; i++) { const start = performance.now() let result for (let i = 0; i < iterations; i++) { result = i % 2 === 0 ? dotVectors(v1, w1) : dotVectors(v2, w2) } const end = performance.now() console.log(`Time for ${iterations} iterations: ${end - start}ms`) }