Add ol' N-body benchmark to examples fwiw

This commit is contained in:
dcodeIO 2018-04-23 02:43:03 +02:00
parent 2ff1bb745a
commit 80d104201c
11 changed files with 10148 additions and 0 deletions

27
examples/n-body/README.md Normal file
View File

@ -0,0 +1,27 @@
N-Body-System
=============
An [AssemblyScript](http://assemblyscript.org) example. This is actually a benchmark - visualizing it just so happened.
Instructions
------------
First, install the development dependencies:
```
$> npm install
```
Now, to build [assembly/index.ts](./assembly/index.ts) to an untouched and an optimized `.wasm` including their respective `.wat` representations, run:
```
$> npm run asbuild
```
Afterwards, run `npm run server` to start a <a href="http://localhost:9080">local server</a>. Should also automatically launch a browser.
To run the benchmark:
```
$> npm run test [steps=1000000]
```

View File

@ -0,0 +1,202 @@
import "allocator/arena";
const SOLAR_MASS = 4.0 * Math.PI * Math.PI;
const DAYS_PER_YEAR = 365.24;
class Body {
constructor(
public x: f64,
public y: f64,
public z: f64,
public vx: f64,
public vy: f64,
public vz: f64,
public mass: f64
) {}
offsetMomentum(px: f64, py: f64, pz: f64): this {
this.vx = -px / SOLAR_MASS;
this.vy = -py / SOLAR_MASS;
this.vz = -pz / SOLAR_MASS;
return this;
}
}
function Sun(): Body {
return new Body(
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, SOLAR_MASS
);
}
function Jupiter(): Body {
return new Body(
4.84143144246472090e+00,
-1.16032004402742839e+00,
-1.03622044471123109e-01,
1.66007664274403694e-03 * DAYS_PER_YEAR,
7.69901118419740425e-03 * DAYS_PER_YEAR,
-6.90460016972063023e-05 * DAYS_PER_YEAR,
9.54791938424326609e-04 * SOLAR_MASS
);
}
function Saturn(): Body {
return new Body(
8.34336671824457987e+00,
4.12479856412430479e+00,
-4.03523417114321381e-01,
-2.76742510726862411e-03 * DAYS_PER_YEAR,
4.99852801234917238e-03 * DAYS_PER_YEAR,
2.30417297573763929e-05 * DAYS_PER_YEAR,
2.85885980666130812e-04 * SOLAR_MASS
);
}
function Uranus(): Body {
return new Body(
1.28943695621391310e+01,
-1.51111514016986312e+01,
-2.23307578892655734e-01,
2.96460137564761618e-03 * DAYS_PER_YEAR,
2.37847173959480950e-03 * DAYS_PER_YEAR,
-2.96589568540237556e-05 * DAYS_PER_YEAR,
4.36624404335156298e-05 * SOLAR_MASS
);
}
function Neptune(): Body {
return new Body(
1.53796971148509165e+01,
-2.59193146099879641e+01,
1.79258772950371181e-01,
2.68067772490389322e-03 * DAYS_PER_YEAR,
1.62824170038242295e-03 * DAYS_PER_YEAR,
-9.51592254519715870e-05 * DAYS_PER_YEAR,
5.15138902046611451e-05 * SOLAR_MASS
);
}
class NBodySystem {
bodies: Body[];
constructor(bodies: Body[]) {
var px = 0.0;
var py = 0.0;
var pz = 0.0;
var size = bodies.length;
for (let i = 0; i < size; i++) {
let b = bodies[i];
let m = b.mass;
px += b.vx * m;
py += b.vy * m;
pz += b.vz * m;
}
this.bodies = bodies;
this.bodies[0].offsetMomentum(px, py, pz);
}
advance(dt: f64): void {
var bodies = this.bodies;
var size = bodies.length;
for (let i = 0; i < size; ++i) {
let bodyi = bodies[i];
let ix = bodyi.x;
let iy = bodyi.y;
let iz = bodyi.z;
let bivx = bodyi.vx;
let bivy = bodyi.vy;
let bivz = bodyi.vz;
let bodyim = bodyi.mass;
for (let j = i + 1; j < size; ++j) {
let bodyj = bodies[j];
let dx = ix - bodyj.x;
let dy = iy - bodyj.y;
let dz = iz - bodyj.z;
let distanceSq = dx * dx + dy * dy + dz * dz;
let distance = sqrt(distanceSq);
let mag = dt / (distanceSq * distance);
let bim = bodyim * mag;
let bjm = bodyj.mass * mag;
bivx -= dx * bjm;
bivy -= dy * bjm;
bivz -= dz * bjm;
bodyj.vx += dx * bim;
bodyj.vy += dy * bim;
bodyj.vz += dz * bim;
}
bodyi.vx = bivx;
bodyi.vy = bivy;
bodyi.vz = bivz;
bodyi.x += dt * bivx;
bodyi.y += dt * bivy;
bodyi.z += dt * bivz;
}
}
energy(): f64 {
var e = 0.0;
var bodies = this.bodies;
var size = bodies.length;
for (let i = 0; i < size; ++i) {
let bodyi = bodies[i];
let ix = bodyi.x;
let iy = bodyi.y;
let iz = bodyi.z;
let vx = bodyi.vx;
let vy = bodyi.vy;
let vz = bodyi.vz;
let bim = bodyi.mass;
e += 0.5 * bim * (vx * vx + vy * vy + vz * vz);
for (let j = i + 1; j < size; ++j) {
let bodyj = bodies[j];
let dx = ix - bodyj.x;
let dy = iy - bodyj.y;
let dz = iz - bodyj.z;
let distance = sqrt(dx * dx + dy * dy + dz * dz);
e -= bim * bodyj.mass / distance;
}
}
return e;
}
}
var bodies = new Array<Body>();
bodies.push(Sun());
bodies.push(Jupiter());
bodies.push(Saturn());
bodies.push(Uranus());
bodies.push(Neptune());
var system = new NBodySystem(bodies);
export function getBody(index: i32): Body | null {
return index < bodies.length ? bodies[index] : null;
}
export function step(): f64 {
system.advance(0.01);
return system.energy();
}
export function bench(steps: i32): f64 {
for (let i = 0; i < steps; i++) system.advance(0.01);
return system.energy();
}

View File

@ -0,0 +1,6 @@
{
"extends": "../../../std/assembly.d.ts",
"include": [
"./**/*.ts"
]
}

3
examples/n-body/build/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.wasm
*.wasm.map
*.asm.js

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

109
examples/n-body/index.html Normal file
View File

@ -0,0 +1,109 @@
<!DOCTYPE html>
<html>
<head>
<title>N-body system - AssemblyScript</title>
<link rel="icon" href="http://assemblyscript.org/favicon.ico" type="image/x-icon" />
<meta name="viewport" content="user-scalable=0" />
<style>
html, body { height: 100%; margin: 0; overflow: hidden; color: #111; background: #fff; font-family: sans-serif; }
body { border-top: 2px solid #070809; }
h1 { padding: 18px 20px 20px; font-size: 12pt; margin: 0; }
a { color: #111; text-decoration: none; }
a:hover { color: #efbd03; text-decoration: underline; }
canvas { position: absolute; top: 60px; left: 20px; width: calc(100% - 40px); height: calc(100% - 80px); background: #070809; }
</style>
</head>
<body>
<h1>
<a href="https://en.wikipedia.org/wiki/N-body_problem">N-body system</a> in
<a href="http://assemblyscript.org">AssemblyScript</a>
( <a href="https://github.com/AssemblyScript/assemblyscript/blob/master/examples/n-body/assembly/index.ts">source</a> )
</h1>
<canvas></canvas>
<script>"use strict";
// Set up the canvas with a 2D rendering context
var cnv = document.getElementsByTagName("canvas")[0];
var ctx = cnv.getContext("2d");
var bcr = cnv.getBoundingClientRect();
// Compute the size of the universe (here: 2px per cell)
var width = bcr.width;
var height = bcr.height;
cnv.width = width;
cnv.height = height;
ctx.imageSmoothingEnabled = false;
// Fetch and instantiate the module
fetch("build/optimized.wasm")
.then(response => response.arrayBuffer())
.then(buffer => WebAssembly.instantiate(buffer, {
env: { abort: function() {} }
}))
.then(module => {
var exports = module.instance.exports;
var mem = new Float64Array(exports.memory.buffer);
// Update about 30 times a second
(function update() {
setTimeout(update, 1000 / 30);
for (var i = 0; i < 3; ++i) exports.step();
})();
// Add some random stars because stars
var stars = [];
for (var i = 0; i < 250; ++i) {
var star = {
x: Math.random() * width,
y: Math.random() * height,
r: 0.2 + Math.random() * 0.8
};
stars.push(star);
}
// Keep rendering
(function render() {
requestAnimationFrame(render);
var cx = width / 2;
var cy = height / 2;
ctx.clearRect(0, 0, width, height);
for (let i = 0, k = stars.length; i < k; ++i) {
let star = stars[i];
ctx.fillStyle = "#fff";
ctx.beginPath();
ctx.globalAlpha = 0.5 + 0.5 * Math.random();
ctx.arc(star.x, star.y, star.r, 0, 2 * Math.PI);
ctx.fill();
}
ctx.globalAlpha = 1.0;
for (let i = 0;; ++i) {
let body = exports.getBody(i);
if (!body) break;
let ptr = body >>> 3;
let x = mem[ptr];
let y = mem[ptr + 1];
let z = mem[ptr + 2];
let m = mem[ptr + 6];
ctx.fillStyle = planets[i].color;
ctx.beginPath();
ctx.arc(cx + x * 10, cy + y * 10, 2 * planets[i].r, 0, 2 * Math.PI);
ctx.fill();
}
})();
}).catch(err => {
alert("Failed to load WASM: " + err.message + " (ad blocker, maybe?)");
console.log(err.stack);
});
var planets = [
{ color: "#f7d864", r: 8.00 },
{ color: "#e2b37d", r: Math.sqrt(11.21) },
{ color: "#cdb086", r: Math.sqrt(9.45) },
{ color: "#588bce", r: Math.sqrt(4.01) },
{ color: "#0f6ab0", r: Math.sqrt(3.88) }
];
</script>
</body>
</html>

8
examples/n-body/index.js Normal file
View File

@ -0,0 +1,8 @@
const fs = require("fs");
const compiled = new WebAssembly.Module(fs.readFileSync(__dirname + "/build/optimized.wasm"));
const imports = {
env: { abort: function() { throw Error("abort called"); } }
};
Object.defineProperty(module, "exports", {
get: () => new WebAssembly.Instance(compiled, imports).exports
});

View File

@ -0,0 +1,15 @@
{
"name": "@assemblyscript/n-body-example",
"version": "1.0.0",
"private": true,
"scripts": {
"asbuild:untouched": "asc assembly/index.ts -b build/untouched.wasm -t build/untouched.wat --sourceMap --validate",
"asbuild:optimized": "asc assembly/index.ts -b build/optimized.wasm -t build/optimized.wat -O3 --sourceMap --validate --noDebug --noAssert",
"asbuild": "npm run asbuild:untouched && npm run asbuild:optimized",
"server": "http-server . -o -c-1",
"test": "node tests"
},
"devDependencies": {
"http-server": "^0.11.1"
}
}

View File

@ -0,0 +1,10 @@
var nbody = require("..");
var steps = process.argv.length > 2 ? parseInt(process.argv[2], 10) : 1000000;
console.log("Performing " + steps + " steps ...");
var start = process.hrtime();
var energy = nbody.bench(steps);
var time = process.hrtime(start);
console.log("Took " + (time[0] * 1e3 + time[1] / 1e6) + "ms (energy=" + energy + ")");