Faster TypedArrays: Vector Addition in WebAssembly
jott.live
jott.live
4x unwrap: https://jsfiddle.net/49j7htdz/1/
8x unwrap: https://jsfiddle.net/49j7htdz/2/
That being said, I’m seeing a very similar relative difference in your fiddle to what I see in the benchmark I posted.
No kidding, this can be seriously misleading.
IMO JS (and likely many other JIT VM) benchmarks should run:
- multiple cold starts
- multiple pre-warmed starts (essentially run your bench twice, discard the results, run it again)
- isolated cases
- shared runtime cases with evenly distributed ordering
All of those will reveal different performance characteristics you may want to know about, and people evaluating your results should be able to know about. Cold starts are a worst case scenario of each implementation, warmed should show a more ideal outcome. Isolated will again highlight the perf of each implementation on its own merits, whereas shared runtime should help highlight how interacting with other real-world code affects VM optimization of that implementation.
There are a few clickable benchmark examples in the readme: https://github.com/xpl/what-code-is-faster#what-code-is-fast...
Reminds me of prof. Emery Berger's talk, Performance Matters: https://www.youtube-nocookie.com/embed/r-TLSBdHe1A
Even with a warmup, the unwrapped version is more than 3x faster.
Largely, the use modern javascript and proper benchmarking made the results more consistent with the benchmark posted.
This included
1) avoiding the use of "var" (using let for scoped iterator variables and const for unchanging references)
2) using proper warmup (10% of runs)
3) isolating the benchmarked code into a function that can be invoked within a loop
4) reseting the values of the arrays being added between benchmarks (to prevent any NaNs/infinities from cropping up)
Inserting the loop into a function really speeds it up in Chrome. Also, I have noticed, that for Float32Array, the rolled / unrolled performance is almost identical, but for Uint32Array, unrolled is about 30% faster: https://jsfiddle.net/aq06p94f/
That's pretty opaquely rude, just FYI.
Same machine, JSC (Safari) is 3x faster than Chrome https://jsbenchit.org/?src=c792550e65de1d038b1f24b446c74592
[1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
I didn’t know about fround but I suspect its primary use case is for trying to catch floating point overflow during double arithmetic, because again precision difference is observable.
function add() {
var i = 0, d0 = 0.0, d1 = 0.0, d2=0.0, d3=0.0;
for (i = 0|0; i < N; i = ((i|0)+(4|0))|0) {
d0 = a[i^0] + b[i^0];
d1 = a[i^1] + b[i^1];
d2 = a[i^2] + b[i^2];
d3 = a[i^3] + b[i^3];
c[i^0] = d0;
c[i^1] = d1;
c[i^2] = d2;
c[i^3] = d3;
}
}
Could you try it & possibly adding this (possibly instead of the slower unrolling options?)I get 1.5mil iterations in numpy on CPU which is on par with typed arrays and much slower than wasmblr.
If you use larger arrays (and use the "out" variant: `numpy.add(a, b, out=c)`), you might get similar total throughput at least.
import numpy as np
N = 1024 * 128
A = np.random.randn(N)
B = np.random.randn(N)
C = np.random.randn(N)
import time
for _ in range(1000):
np.add(A, B, out=C)
iters = 1000
t = time.time()
for _ in range(iters):
np.add(A, B, out=C)
d = time.time() - t
print(f"{iters * N * 3 * 4 / d / 1e9:.2f} GB/s")What is the current state of these -- do simd wasm ops run in ffox/chrome/edge/safari, and does the data have to be slowly bulk copied back-and-forth from a worker (heartbleed removal of zero-copy ownership transfers)?
We love js typed arrays and columnar analytics -- graphistry contributed the first years of Apache Arrow JS -- but intentionally didn't do wasm for kernels because of this kind of stuff, despite promising internal multicore etc prototypes. A surprise win for us of GPU python/js offloading to the server has been not just scale but perf reliability. Curious how much it has improved in the typical case, as it always made sense on paper!
I've found the cleanest approach for me is to have modules allocate space for inputs and outputs and then try to get functions to write directly into the input space.
Either way, nothing available is super friendly for user provided arrays or canvas interactions.
Mostly, from what I've seen, it tunes to a value greater than 1. So I think generally it's still necessary today.