mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-06-09 13:01:26 +00:00
Add ol' N-body benchmark to examples fwiw
This commit is contained in:
parent
2ff1bb745a
commit
80d104201c
27
examples/n-body/README.md
Normal file
27
examples/n-body/README.md
Normal 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]
|
||||||
|
```
|
202
examples/n-body/assembly/index.ts
Normal file
202
examples/n-body/assembly/index.ts
Normal 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();
|
||||||
|
}
|
6
examples/n-body/assembly/tsconfig.json
Normal file
6
examples/n-body/assembly/tsconfig.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../../std/assembly.d.ts",
|
||||||
|
"include": [
|
||||||
|
"./**/*.ts"
|
||||||
|
]
|
||||||
|
}
|
3
examples/n-body/build/.gitignore
vendored
Normal file
3
examples/n-body/build/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
*.wasm
|
||||||
|
*.wasm.map
|
||||||
|
*.asm.js
|
BIN
examples/n-body/build/optimized.wasm
Normal file
BIN
examples/n-body/build/optimized.wasm
Normal file
Binary file not shown.
4478
examples/n-body/build/optimized.wat
Normal file
4478
examples/n-body/build/optimized.wat
Normal file
File diff suppressed because it is too large
Load Diff
5290
examples/n-body/build/untouched.wat
Normal file
5290
examples/n-body/build/untouched.wat
Normal file
File diff suppressed because it is too large
Load Diff
109
examples/n-body/index.html
Normal file
109
examples/n-body/index.html
Normal 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
8
examples/n-body/index.js
Normal 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
|
||||||
|
});
|
15
examples/n-body/package.json
Normal file
15
examples/n-body/package.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
10
examples/n-body/tests/index.js
Normal file
10
examples/n-body/tests/index.js
Normal 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 + ")");
|
Loading…
x
Reference in New Issue
Block a user