Skip to content

Instantly share code, notes, and snippets.

@dherman
Last active January 9, 2020 23:28
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dherman/3d0b4733303eaf4bae5e to your computer and use it in GitHub Desktop.
Save dherman/3d0b4733303eaf4bae5e to your computer and use it in GitHub Desktop.
calculating the geometric mean in normal JS and asm.js
// Calculating the geometric mean in log space avoids overflow when
// working with anything more than very small data sets.
//
// The formula can be calculated with simple algebraic identities:
//
// n√(Prod^n x_i)
// = { e^ln(x) = x }
// n√(Prod^n e^ln(x_i))
// = { e^a x e^b = e^(a+b) }
// n√(e^(Sum^n ln(x_i)))
// = { z√(x^y) = x^(y / z) }
// e((Sum^n ln(x_i)) / n)
function PlainModule(buffer) {
var values = new Float64Array(buffer);
function logSum(start, end) {
var sum = 0;
for (var i = start; i < end; i++) {
sum += Math.log(values[i]);
}
return sum;
}
function geometricMean(start, end) {
return Math.exp(logSum(start, end) / (end - start));
}
return { geometricMean: geometricMean };
}
function ASMModule(stdlib, foreign, buffer) {
"use asm";
var exp = stdlib.Math.exp;
var log = stdlib.Math.log;
var values = new stdlib.Float64Array(buffer);
function logSum(start, end) {
start = start|0;
end = end|0;
var sum = 0.0, p = 0, q = 0;
// asm.js forces byte addressing of the heap by requiring shifting by 3
for (p = start << 3, q = end << 3; (p|0) < (q|0); p = (p + 8)|0) {
sum = sum + +log(values[p>>3]);
}
return +sum;
}
function geometricMean(start, end) {
start = start|0;
end = end|0;
return +exp(+logSum(start, end) / +((end - start)|0));
}
return { geometricMean: geometricMean };
}
var fakeWindow = {
Float64Array: Float64Array,
Math: {
log: (x) => Math.log(x),
exp: (x) => Math.exp(x)
}
};
function time(thunk) {
var start = performance.now();
var result = thunk();
var end = performance.now();
return {
result: result,
time: end - start
};
}
function build(length) {
var result = new Float64Array(length);
for (var i = 0; i < length; i++) {
result[i] = i + 1;
}
return result;
}
var values = build(0x10000);
var fast = ASMModule(window, null, values.buffer);
var slow = ASMModule(fakeWindow, null, values.buffer);
var plain = PlainModule(values.buffer);
console.log(time(() => fast.geometricMean(0, 0x10000)));
console.log(time(() => slow.geometricMean(0, 0x10000)));
console.log(time(() => plain.geometricMean(0, 0x10000)));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment