blob: d6380351267d3170ff0ec60bd567e18df2b8923a [file] [log] [blame]
/// Copyright 2013 the V8 project authors. All rights reserved.
/// Copyright 2009 Oliver Hunt <http://nerget.com>
///
/// Permission is hereby granted, free of charge, to any person
/// obtaining a copy of this software and associated documentation
/// files (the "Software"), to deal in the Software without
/// restriction, including without limitation the rights to use,
/// copy, modify, merge, publish, distribute, sublicense, and/or sell
/// copies of the Software, and to permit persons to whom the
/// Software is furnished to do so, subject to the following
/// conditions:
///
/// The above copyright notice and this permission notice shall be
/// included in all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
/// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
/// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
/// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
/// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
/// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
/// OTHER DEALINGS IN THE SOFTWARE.
// Ported from the v8 benchmark suite by Google 2013.
// Uses Float64List for data.
import 'dart:math';
import 'dart:typed_data';
import 'package:benchmark_harness/benchmark_harness.dart';
/// Copyright 2009 Oliver Hunt <http://nerget.com>
///
/// Permission is hereby granted, free of charge, to any person
/// obtaining a copy of this software and associated documentation
/// files (the "Software"), to deal in the Software without
/// restriction, including without limitation the rights to use,
/// copy, modify, merge, publish, distribute, sublicense, and/or sell
/// copies of the Software, and to permit persons to whom the
/// Software is furnished to do so, subject to the following
/// conditions:
///
/// The above copyright notice and this permission notice shall be
/// included in all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
/// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
/// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
/// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
/// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
/// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
/// OTHER DEALINGS IN THE SOFTWARE.
// Ported from the v8 benchmark suite by Google 2013.
// Uses Float64List for data.
/// Copyright 2009 Oliver Hunt <http://nerget.com>
///
/// Permission is hereby granted, free of charge, to any person
/// obtaining a copy of this software and associated documentation
/// files (the "Software"), to deal in the Software without
/// restriction, including without limitation the rights to use,
/// copy, modify, merge, publish, distribute, sublicense, and/or sell
/// copies of the Software, and to permit persons to whom the
/// Software is furnished to do so, subject to the following
/// conditions:
///
/// The above copyright notice and this permission notice shall be
/// included in all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
/// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
/// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
/// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
/// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
/// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
/// OTHER DEALINGS IN THE SOFTWARE.
// Ported from the v8 benchmark suite by Google 2013.
// Uses Float64List for data.
main() {
const FluidMotion().report();
}
class FluidMotion extends BenchmarkBase {
static FluidField solver;
static int framesTillAddingPoints = 0;
static int framesBetweenAddingPoints = 5;
const FluidMotion() : super('FluidMotion');
static void setupFluidMotion() {
framesTillAddingPoints = 0;
framesBetweenAddingPoints = 5;
solver = FluidField.create(null, 128, 128, 20);
solver.setDisplayFunction((a) {});
solver.setUICallback(prepareFrame);
}
static void runFluidMotion() {
setupFluidMotion();
for (int i = 0; i < 10; i++) {
solver.update();
}
solver.validate(758.9012130174812, -352.56376676179076, -357.3690235879736);
}
static void main() {
runFluidMotion();
}
static void addPoints(Field field) {
var n = 64;
for (var i = 1; i <= n; i++) {
field.setVelocity(i, i, n.toDouble(), n.toDouble());
field.setDensity(i, i, 5.0);
field.setVelocity(i, n - i, -n.toDouble(), -n.toDouble());
field.setDensity(i, n - i, 20.0);
field.setVelocity(128 - i, n + i, -n.toDouble(), -n.toDouble());
field.setDensity(128 - i, n + i, 30.0);
}
}
static void prepareFrame(Field field) {
if (framesTillAddingPoints == 0) {
addPoints(field);
framesTillAddingPoints = framesBetweenAddingPoints;
framesBetweenAddingPoints++;
} else {
framesTillAddingPoints--;
}
}
// Overrides of BenchmarkBase.
void warmup() {
runFluidMotion();
}
void exercise() {
runFluidMotion();
}
}
// Code from Oliver Hunt (http://nerget.com/fluidSim/pressure.js) starts here.
class FluidField {
final canvas;
final int iterations;
final double dt = 0.1;
final int size;
Float64List dens, dens_prev;
Float64List u, u_prev;
Float64List v, v_prev;
final int width, height;
final int rowSize;
var displayFunc;
static FluidField _lastCreated;
static bool approxEquals(double a, double b) => (a - b).abs() < 0.000001;
validate(expectedDens, expectedU, expectedV) {
var sumDens = 0.0;
var sumU = 0.0;
var sumV = 0.0;
for (int i = 0; i < dens.length; i++) {
sumDens += dens[i];
sumU += u[i];
sumV += v[i];
}
if (!approxEquals(sumDens, expectedDens) ||
!approxEquals(sumU, expectedU) ||
!approxEquals(sumV, expectedV)) {
throw "Incorrect result";
}
}
// Allocates a new FluidField or return previously allocated field if the
// size is too large.
factory FluidField.create(canvas, int hRes, int wRes, int iterations) {
final res = wRes * hRes;
if ((res > 0) && (res < 1000000)) {
_lastCreated = FluidField(canvas, hRes, wRes, iterations);
}
_lastCreated ??= FluidField(canvas, 64, 64, iterations);
assert((canvas == _lastCreated.canvas) &&
(iterations == _lastCreated.iterations));
return _lastCreated;
}
FluidField(this.canvas, this.height, this.width, this.iterations)
: rowSize = width + 2,
size = (width + 2) * (height + 2) {
reset();
}
void reset() {
// All Float64List elements are initialized to 0.0.
dens = Float64List(size);
dens_prev = Float64List(size);
u = Float64List(size);
u_prev = Float64List(size);
v = Float64List(size);
v_prev = Float64List(size);
}
void addFields(Float64List x, Float64List s, double dt) {
for (var i = 0; i < size; i++) {
x[i] += dt * s[i];
}
}
void set_bnd(int b, Float64List x) {
if (b == 1) {
var i = 1;
for (; i <= width; i++) {
x[i] = x[i + rowSize];
x[i + (height + 1) * rowSize] = x[i + height * rowSize];
}
for (var j = 1; j <= height; j++) {
x[j * rowSize] = -x[1 + j * rowSize];
x[(width + 1) + j * rowSize] = -x[width + j * rowSize];
}
} else if (b == 2) {
for (var i = 1; i <= width; i++) {
x[i] = -x[i + rowSize];
x[i + (height + 1) * rowSize] = -x[i + height * rowSize];
}
for (var j = 1; j <= height; j++) {
x[j * rowSize] = x[1 + j * rowSize];
x[(width + 1) + j * rowSize] = x[width + j * rowSize];
}
} else {
for (var i = 1; i <= width; i++) {
x[i] = x[i + rowSize];
x[i + (height + 1) * rowSize] = x[i + height * rowSize];
}
for (var j = 1; j <= height; j++) {
x[j * rowSize] = x[1 + j * rowSize];
x[(width + 1) + j * rowSize] = x[width + j * rowSize];
}
}
var maxEdge = (height + 1) * rowSize;
x[0] = 0.5 * (x[1] + x[rowSize]);
x[maxEdge] = 0.5 * (x[1 + maxEdge] + x[height * rowSize]);
x[(width + 1)] = 0.5 * (x[width] + x[(width + 1) + rowSize]);
x[(width + 1) + maxEdge] =
0.5 * (x[width + maxEdge] + x[(width + 1) + height * rowSize]);
}
void lin_solve(int b, Float64List x, Float64List x0, int a, int c) {
if (a == 0 && c == 1) {
for (var j = 1; j <= height; j++) {
var currentRow = j * rowSize;
++currentRow;
for (var i = 0; i < width; i++) {
x[currentRow] = x0[currentRow];
++currentRow;
}
}
set_bnd(b, x);
} else {
var invC = 1 / c;
for (var k = 0; k < iterations; k++) {
for (var j = 1; j <= height; j++) {
var lastRow = (j - 1) * rowSize;
var currentRow = j * rowSize;
var nextRow = (j + 1) * rowSize;
var lastX = x[currentRow];
++currentRow;
for (var i = 1; i <= width; i++) {
lastX = x[currentRow] = (x0[currentRow] +
a *
(lastX +
x[++currentRow] +
x[++lastRow] +
x[++nextRow])) *
invC;
}
}
set_bnd(b, x);
}
}
}
void diffuse(int b, Float64List x, Float64List x0, double dt) {
var a = 0;
lin_solve(b, x, x0, a, 1 + 4 * a);
}
void lin_solve2(Float64List x, Float64List x0, Float64List y, Float64List y0,
int a, int c) {
if (a == 0 && c == 1) {
for (var j = 1; j <= height; j++) {
var currentRow = j * rowSize;
++currentRow;
for (var i = 0; i < width; i++) {
x[currentRow] = x0[currentRow];
y[currentRow] = y0[currentRow];
++currentRow;
}
}
set_bnd(1, x);
set_bnd(2, y);
} else {
var invC = 1 / c;
for (var k = 0; k < iterations; k++) {
for (var j = 1; j <= height; j++) {
var lastRow = (j - 1) * rowSize;
var currentRow = j * rowSize;
var nextRow = (j + 1) * rowSize;
var lastX = x[currentRow];
var lastY = y[currentRow];
++currentRow;
for (var i = 1; i <= width; i++) {
lastX = x[currentRow] = (x0[currentRow] +
a * (lastX + x[currentRow] + x[lastRow] + x[nextRow])) *
invC;
lastY = y[currentRow] = (y0[currentRow] +
a *
(lastY +
y[++currentRow] +
y[++lastRow] +
y[++nextRow])) *
invC;
}
}
set_bnd(1, x);
set_bnd(2, y);
}
}
}
void diffuse2(Float64List x, Float64List x0, y, Float64List y0, double dt) {
var a = 0;
lin_solve2(x, x0, y, y0, a, 1 + 4 * a);
}
void advect(int b, Float64List d, Float64List d0, Float64List u,
Float64List v, double dt) {
var Wdt0 = dt * width;
var Hdt0 = dt * height;
var Wp5 = width + 0.5;
var Hp5 = height + 0.5;
for (var j = 1; j <= height; j++) {
var pos = j * rowSize;
for (var i = 1; i <= width; i++) {
var x = i - Wdt0 * u[++pos];
var y = j - Hdt0 * v[pos];
if (x < 0.5) {
x = 0.5;
} else if (x > Wp5) x = Wp5;
var i0 = x.toInt();
var i1 = i0 + 1;
if (y < 0.5) {
y = 0.5;
} else if (y > Hp5) y = Hp5;
var j0 = y.toInt();
var j1 = j0 + 1;
var s1 = x - i0;
var s0 = 1 - s1;
var t1 = y - j0;
var t0 = 1 - t1;
var row1 = j0 * rowSize;
var row2 = j1 * rowSize;
d[pos] = s0 * (t0 * d0[i0 + row1] + t1 * d0[i0 + row2]) +
s1 * (t0 * d0[i1 + row1] + t1 * d0[i1 + row2]);
}
}
set_bnd(b, d);
}
void project(Float64List u, Float64List v, Float64List p, Float64List div) {
var h = -0.5 / sqrt(width * height);
for (var j = 1; j <= height; j++) {
var row = j * rowSize;
var previousRow = (j - 1) * rowSize;
var prevValue = row - 1;
var currentRow = row;
var nextValue = row + 1;
var nextRow = (j + 1) * rowSize;
for (var i = 1; i <= width; i++) {
div[++currentRow] = h *
(u[++nextValue] - u[++prevValue] + v[++nextRow] - v[++previousRow]);
p[currentRow] = 0.0;
}
}
set_bnd(0, div);
set_bnd(0, p);
lin_solve(0, p, div, 1, 4);
var wScale = 0.5 * width;
var hScale = 0.5 * height;
for (var j = 1; j <= height; j++) {
var prevPos = j * rowSize - 1;
var currentPos = j * rowSize;
var nextPos = j * rowSize + 1;
var prevRow = (j - 1) * rowSize;
var nextRow = (j + 1) * rowSize;
for (var i = 1; i <= width; i++) {
u[++currentPos] -= wScale * (p[++nextPos] - p[++prevPos]);
v[currentPos] -= hScale * (p[++nextRow] - p[++prevRow]);
}
}
set_bnd(1, u);
set_bnd(2, v);
}
void dens_step(
Float64List x, Float64List x0, Float64List u, Float64List v, double dt) {
addFields(x, x0, dt);
diffuse(0, x0, x, dt);
advect(0, x, x0, u, v, dt);
}
void vel_step(
Float64List u, Float64List v, Float64List u0, Float64List v0, double dt) {
addFields(u, u0, dt);
addFields(v, v0, dt);
var temp = u0;
u0 = u;
u = temp;
temp = v0;
v0 = v;
v = temp;
diffuse2(u, u0, v, v0, dt);
project(u, v, u0, v0);
temp = u0;
u0 = u;
u = temp;
temp = v0;
v0 = v;
v = temp;
advect(1, u, u0, u0, v0, dt);
advect(2, v, v0, u0, v0, dt);
project(u, v, u0, v0);
}
var uiCallback;
void setDisplayFunction(func) {
displayFunc = func;
}
void setUICallback(callback) {
uiCallback = callback;
}
void queryUI(Float64List d, Float64List u, Float64List v) {
for (var i = 0; i < size; i++) {
u[i] = v[i] = d[i] = 0.0;
}
uiCallback(Field(d, u, v, rowSize));
}
void update() {
queryUI(dens_prev, u_prev, v_prev);
vel_step(u, v, u_prev, v_prev, dt);
dens_step(dens, dens_prev, u, v, dt);
displayFunc(Field(dens, u, v, rowSize));
}
}
// Difference from JS version: Field takes an argument rowSize, but this
// used for display purpose only.
class Field {
final Float64List dens, u, v;
final int rowSize;
Field(this.dens, this.u, this.v, this.rowSize);
void setDensity(int x, int y, double d) {
dens[(x + 1) + (y + 1) * rowSize] = d;
}
double getDensity(int x, int y) {
return dens[(x + 1) + (y + 1) * rowSize]; // rowSize from FluidField?
}
void setVelocity(int x, int y, double xv, double yv) {
u[(x + 1) + (y + 1) * rowSize] = xv;
v[(x + 1) + (y + 1) * rowSize] = yv;
}
double getXVelocity(int x, int y) {
return u[(x + 1) + (y + 1) * rowSize];
}
double getYVelocity(int x, int y) {
return v[(x + 1) + (y + 1) * rowSize];
}
}