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