Skip to content

Instantly share code, notes, and snippets.

@postspectacular
Last active November 2, 2019 20:31
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 postspectacular/3dccbfed1b753edadf1b6fee8add4808 to your computer and use it in GitHub Desktop.
Save postspectacular/3dccbfed1b753edadf1b6fee8add4808 to your computer and use it in GitHub Desktop.
Sigmoid versions (AssemblyScript)
// uses Math.exp() approximation from:
// https://www.musicdsp.org/en/latest/Other/222-fast-exp-approximations.html
// @ts-ignore: decorator
@inline
function fastexp9(x: f64): f64 {
// prettier-ignore
return (362880+x*(362880+x*(181440+x*(60480+x*(15120+x*(3024+x*(504+x*(72+x*(9+x)))))))))*2.75573192e-6;
}
// @ts-ignore: decorator
@inline
function sigmoidApprox(x: f64): f64 {
return 1.0 / (1.0 + fastexp9(-x));
}
// @ts-ignore: decorator
@inline
function sigmoidDerivApprox(x: f64): f64 {
x = fastexp9(-x);
const y = 1.0 + x;
return x / (y * y);
}
export function sigmoidApproxPtr(out: usize, src: usize, n: usize): usize {
const res = out;
while (n-- > 0) {
f64.store(out, sigmoidApprox(f64.load(src)));
out += sizeof<f64>(); // 8
src += sizeof<f64>(); // 8
}
return res;
}
export function sigmoidDerivApproxPtr(out: usize, src: usize, n: usize): usize {
const res = out;
while (n-- > 0) {
f64.store(out, sigmoidDerivApprox(f64.load(src)));
out += sizeof<f64>(); // 8
src += sizeof<f64>(); // 8
}
return res;
}
; disassembled WASM of the above fn
; only math ops in the loop, no fn calls...
(func $assembly/index/sigmoidApproxPtr (; 3 ;) (type $FUNCSIG$iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
(local $3 f64)
(local $4 i32)
(local $5 i32)
local.get $0
loop $continue|0
local.get $2
local.tee $5
i32.const 1
i32.sub
local.set $2
local.get $5
i32.const 0
i32.lt_u
i32.eqz
if
local.get $0
f64.const 1
f64.const 1
f64.const 362880
local.get $1
f64.load
f64.neg
local.tee $3
f64.const 362880
local.get $3
f64.const 181440
local.get $3
f64.const 60480
local.get $3
f64.const 15120
local.get $3
f64.const 3024
local.get $3
f64.const 504
local.get $3
f64.const 72
local.get $3
f64.const 9
local.get $3
f64.add
f64.mul
f64.add
f64.mul
f64.add
f64.mul
f64.add
f64.mul
f64.add
f64.mul
f64.add
f64.mul
f64.add
f64.mul
f64.add
f64.mul
f64.add
f64.const 2.75573192e-06
f64.mul
f64.add
f64.div
f64.store
local.get $0
i32.const 8
i32.add
local.set $0
local.get $1
i32.const 8
i32.add
local.set $1
br $continue|0
end
end
)
// Chem's original impl (only renamed) & using "native" math
// see: https://docs.assemblyscript.org/standard-library/math#variants
// @ts-ignore: decorator
@inline
function sigmoidNat(x: f64): f64 {
return 1.0 / (1.0 + Math.exp(-x));
}
// updated to avoid double eval of exp():
// @ts-ignore: decorator
@inline
function sigmoidDerivNat(x: f64): f64 {
x = Math.exp(-x);
const y = 1.0 + x;
return x / (y * y);
}
export function sigmoidNatPtr(out: usize, src: usize, n: usize): usize {
const res = out;
while (n-- > 0) {
f64.store(out, sigmoidNat(f64.load(src)));
out += sizeof<f64>(); // 8
src += sizeof<f64>(); // 8
}
return res;
}
export function sigmoidDerivNatPtr(out: usize, src: usize, n: usize): usize {
const res = out;
while (n-- > 0) {
f64.store(out, sigmoidDerivNat(f64.load(src)));
out += sizeof<f64>(); // 8
src += sizeof<f64>(); // 8
}
return res;
}
// NOT YET SUPPORTED DUE TO MISSING SIMD OP CODE IMPLS in v8:
// https://github.com/WebAssembly/simd/blob/master/proposals/simd/ImplementationStatus.md
// @ts-ignore: decorator
@inline
function addm_64x2(x: v128, y: v128, n: f64): v128 {
return f64x2.mul(x, f64x2.add(y, f64x2.splat(n)));
}
// @ts-ignore: decorator
@inline
function fastexp9_64x2(ptr: usize): v128 {
let x = v128.load(ptr);
let y = addm_64x2(x, x, 9.0);
y = addm_64x2(x, y, 72.0);
y = addm_64x2(x, y, 504.0);
y = addm_64x2(x, y, 3024.0);
y = addm_64x2(x, y, 15120.0);
y = addm_64x2(x, y, 60480.0);
y = addm_64x2(x, y, 181440.0);
y = addm_64x2(x, y, 362880.0);
y = addm_64x2(x, y, 362880.0);
return f64x2.mul(y, f64x2.splat(2.75573192e-6));
}
// @ts-ignore: decorator
@inline
function sigmoid_64x2(x: usize): v128 {
const one = f64x2.splat(1.0);
return f64x2.div(one, f64x2.add(one, fastexp9_64x2(-x)));
}
export function sigmoid_64x2_ptr(out: usize, src: usize, n: usize): usize {
const res = out;
n >>= 1;
while (n-- > 0) {
v128.store(out, sigmoid_64x2(src));
out += sizeof<v128>();
src += sizeof<v128>();
}
return res;
}
// Chem's original loop
export function sigmoidNatArray(x: Array<f64>, derivate: boolean): Array<f64> {
let results: Array<f64> = [];
for (let i = x.length; --i >= 0; ) {
results[i] = derivate ? sigmoidDerivNat(x[i]) : sigmoidNat(x[i]);
}
return results;
}
; Chem's original loop WASM
; uses AssemblyScript's runtime, Array accessor indirection, bounds checks etc.
(func $assembly/index/sigmoidNatArray (; 40 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32)
(local $2 i32)
(local $3 i32)
(local $4 i32)
(local $5 f64)
local.get $0
call $~lib/rt/pure/__retain
drop
call $~lib/rt/__allocArray
call $~lib/rt/pure/__retain
local.tee $4
call $~lib/rt/pure/__retain
local.set $3
local.get $0
i32.load offset=12
local.set $2
loop $loop|0
local.get $2
i32.const 1
i32.sub
local.tee $2
i32.const 0
i32.ge_s
if
local.get $3
local.get $2
local.get $1
if (result f64)
local.get $0
local.get $2
call $~lib/array/Array<f64>#__get
local.tee $5
f64.neg
call $~lib/math/NativeMath.exp
f64.const 1
local.get $5
f64.neg
call $~lib/math/NativeMath.exp
f64.add
call $~lib/math/NativeMath.pow
f64.div
else
f64.const 1
f64.const 1
local.get $0
local.get $2
call $~lib/array/Array<f64>#__get
f64.neg
call $~lib/math/NativeMath.exp
f64.add
f64.div
end
call $~lib/array/Array<f64>#__set
br $loop|0
end
end
local.get $4
call $~lib/rt/pure/__release
local.get $0
call $~lib/rt/pure/__release
local.get $3
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment