You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

129 lines
3.9 KiB
JavaScript

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`)
}