Skip to content

Instantly share code, notes, and snippets.

@zicklag
Last active December 4, 2018 21:50
Show Gist options
  • Save zicklag/8b1bc845bd29678a73814ce75532414d to your computer and use it in GitHub Desktop.
Save zicklag/8b1bc845bd29678a73814ce75532414d to your computer and use it in GitHub Desktop.
Testing Haxe Hotmem for Vector Abstract
package arm;
import haxe.ds.Vector;
import iron.system.Time;
import iron.math.HotVec4;
import iron.math.Vec4;
enum VectorType {
HotVecType;
VecType;
}
class BenchmarkVec4 extends iron.Trait {
public function new() {
super();
// benchmark1(); -- Unuseful benchmark.
benchmark2();
benchmark3();
}
/**
* Unuseful benchmark. Performance is identical.
*/
private function benchmark1() {
trace('----START BENCHMARK: Multipliactions and Additions of two Vectors----');
var iterations = 500000000;
var vec4Time = 0.0;
var hotVecTime = 0.0;
{
trace('Test Vec4');
var startTime = Time.realTime();
var i = 0;
var v1 = new Vec4(Math.random() * 10, Math.random() * 10, Math.random() * 10);
var v2 = new Vec4(Math.random() * 10, Math.random() * 10, Math.random() * 10);
while (i < iterations) {
i++;
v1.add(v2);
v1.mult(12);
v1.mult(1/3);
v2.sub(v1);
v2.normalize();
v2.addvecs(v1, v2);
v2.dot(v2);
v1.cross(v2);
v1.add(v2.mult(2));
}
vec4Time = Time.realTime() - startTime;
trace('That took: $vec4Time');
}
{
#if hotmem_vectors
trace('Test HotVec4 using hotmem');
#else
trace('Test HotVec4 using class/struct backend');
#end
var v1 = new HotVec4(Math.random() * 10, Math.random() * 10, Math.random() * 10);
var v2 = new HotVec4(Math.random() * 10, Math.random() * 10, Math.random() * 10);
var startTime = Time.realTime();
var i = 0;
while (i < iterations) {
i++;
v1.add(v2);
v1.mult(12);
v1.mult(1/3);
v2.sub(v1);
v2.normalize();
v2.addvecs(v1, v2);
v2.dot(v2);
v1.cross(v2);
v1.add(v2.mult(2));
}
hotVecTime = Time.realTime() - startTime;
trace('That took: $hotVecTime');
}
trace('vec4/hotvec ratio: ${vec4Time/hotVecTime}');
}
function benchmark2() {
trace('----START BENCHMARK: Massive allocation-----');
var iterations = 1000000;
var vec4Time = 0.0;
var hotVecTime = 0.0;
{
trace('Test Vec4');
var startTime = Time.realTime();
trace('Creating vectors...');
var vecs = new Vector<Vec4>(iterations);
var i = 0;
while (i < iterations) {
i++;
vecs[i] = new Vec4(Math.random() * 10, Math.random() * 10, Math.random() * 10);
}
vec4Time = Time.realTime() - startTime;
trace('That took: $vec4Time');
}
{
trace('Test HotVec');
var startTime = Time.realTime();
trace('Creating vectors...');
var vecs = new Vector<HotVec4>(iterations);
var i = 0;
while (i < iterations) {
i++;
vecs[i] = new HotVec4(Math.random() * 10, Math.random() * 10, Math.random() * 10);
}
hotVecTime = Time.realTime() - startTime;
trace('That took: $hotVecTime');
}
trace('vec4/hotvec ratio: ${vec4Time/hotVecTime}');
}
function benchmark3() {
trace('----START BENCHMARK: Mutable VS Immutable-----');
var iterations = 100000000;
var mutableTime = 0.0;
var immutableTime = 0.0;
{
var v1 = new HotVec4(Math.random() * 10, Math.random() * 10, Math.random() * 10);
var v2 = new HotVec4(Math.random() * 10, Math.random() * 10, Math.random() * 10);
trace('Test mutable operations');
var startTime = Time.realTime();
var i = 0;
while (i < iterations) {
i++;
v1.add(v2);
v2.mult(Math.random());
v2.sub(v1);
v2.div(Math.random());
}
mutableTime = Time.realTime() - startTime;
trace('That took: $mutableTime');
}
{
var v1 = new HotVec4(Math.random() * 10, Math.random() * 10, Math.random() * 10);
var v2 = new HotVec4(Math.random() * 10, Math.random() * 10, Math.random() * 10);
trace('Test immutable operations');
var startTime = Time.realTime();
var i = 0;
while (i < iterations) {
i++;
v1 + v2;
v2 * Math.random();
v2 - v1;
v2 / Math.random();
}
immutableTime = Time.realTime() - startTime;
trace('That took: $immutableTime');
}
trace('mutable/immutable ratio: ${mutableTime/immutableTime}');
}
}
package iron.math;
import haxe.ds.Vector;
import kha.FastFloat;
import hotmem.F32;
#if hotmem_vectors
import hotmem.F32Array;
#else
#if js
private typedef HotVec4Data = {
x:FastFloat,
y:FastFloat,
z:FastFloat,
w:FastFloat
}
#else
private class HotVec4Data {
public var x:FastFloat;
public var y:FastFloat;
public var z:FastFloat;
public var w:FastFloat;
public function new(x, y, z, w) {
this.x = x;
this.y = y;
this.z = z;
this.w = w;
}
}
#end
#end
#if hotmem_vectors
abstract HotVec4(F32Array) {
#else
abstract HotVec4(HotVec4Data) {
#end
public var x(get, set):F32;
public var y(get, set):F32;
public var z(get, set):F32;
public var w(get, set):F32;
public var length(get, set):F32;
public inline function new(x:F32 = 0.0, y:F32 = 0.0, z:F32 = 0.0, w:F32 = 1.0) {
#if hotmem_vectors
this = new F32Array(4);
#else
#if js
this = {
x: x,
y: y,
z: z,
w: w,
}
#else
this = new HotVec4Data(x, y, z, w);
#end
#end
}
public inline function set(x:F32, y:F32, z:F32, w:F32 = 1.0):HotVec4 {
set_x(x);
set_y(y);
set_z(z);
set_w(w);
return cast this;
}
public inline function setFrom(v:HotVec4):HotVec4 {
x = v.x;
y = v.y;
z = v.z;
w = v.w;
return cast this;
}
public inline function clone():HotVec4 {
return new HotVec4(x, y, z, w);
}
//
// Getters and Setters
//
#if hotmem_vectors
inline function get_x() { return this[0]; }
inline function set_x(x:F32) { this[0] = x; return this[0]; }
inline function get_y() { return this[1]; }
inline function set_y(y:F32) { this[1] = y; return this[1]; }
inline function get_z() { return this[2]; }
inline function set_z(z:F32) { this[2] = z; return this[2]; }
inline function get_w() { return this[3]; }
inline function set_w(w:F32) { this[3] = w; return this[3]; }
#else
inline function get_x() { return this.x; }
inline function set_x(x:F32) { this.x = x; return this.x; }
inline function get_y() { return this.y; }
inline function set_y(y:F32) { this.y = y; return this.y; }
inline function get_z() { return this.z; }
inline function set_z(z:F32) { this.z = z; return this.z; }
inline function get_w() { return this.w; }
inline function set_w(w:F32) { this.w = w; return this.w; }
#end
inline function get_length():F32 {
return Math.sqrt(x* x + y * y + z * z);
}
inline function set_length(length:F32):F32 {
var currentLength = get_length();
if (currentLength > 0.0) {
var factor = length / currentLength;
x *= factor;
y *= factor;
z *= factor;
}
return length;
}
//
// Operator Overloads
//
@:op(a + b)
private inline function _add(b:HotVec4):HotVec4 {
return new HotVec4(
x + b.x,
y + b.y,
z + b.z
);
}
@:op(a += b)
public inline function add(b:HotVec4):HotVec4 {
x += b.x;
y += b.y;
z += b.z;
return cast this;
}
@:op(a + b)
private inline function _addFloat(b:F32):HotVec4 {
return new HotVec4(
x + b,
y + b,
z + b
);
}
@:op(a += b)
private inline function _addEqualsFloat(b:F32):HotVec4 {
x += b;
y += b;
z += b;
return cast this;
}
@:op(a - b)
private inline function _sub(b:HotVec4):HotVec4 {
return new HotVec4(
x - b.x,
y - b.y,
z - b.z
);
}
@:op(a -= b)
public inline function sub(b:HotVec4):HotVec4 {
x -= b.x;
y -= b.y;
z -= b.z;
return cast this;
}
@:op(a - b)
private inline function _subFloat(b:F32):HotVec4 {
return new HotVec4(
x - b,
y - b,
z - b
);
}
@:op(a -= b)
private inline function _subEqualsFloat(b:F32):HotVec4 {
x -= b;
y -= b;
z -= b;
return cast this;
}
@:op(a * b)
private inline function _mult(b:F32):HotVec4 {
return new HotVec4(
x * b,
y * b,
z * b
);
}
@:op(a *= b)
public inline function mult(b:F32):HotVec4 {
x *= b;
y *= b;
x *= b;
return cast this;
}
@:op(a / b)
private inline function _div(b:F32):HotVec4 {
return new HotVec4(
x / b,
y / b,
z / b
);
}
@:op(a /= b)
public inline function div(b:F32):HotVec4 {
x /= b;
y /= b;
x /= b;
return cast this;
}
@:op(a < b)
private inline function _ltVec(b:HotVec4) {
return length < b.length;
}
@:op(a < b)
private inline function _ltFloat(b:F32) {
return length < b;
}
@:op(a > b)
private inline function _gtVec(b:HotVec4) {
return length > b.length;
}
@:op(a > b)
private inline function _gtFloat(b:F32) {
return length > b;
}
@:op(a == b)
public inline function equals(v:HotVec4) {
return x == v.x && y == v.y && z == v.z;
}
//
// Mutator Methods
//
public inline function normalize():HotVec4 {
length = 1;
return cast this;
}
public inline function dot(v:HotVec4):FastFloat {
return x * v.x + y * v.y + z * v.z;
}
public inline function cross(v:HotVec4):HotVec4 {
var ax = x; var ay = y; var az = z;
var vx = v.x; var vy = v.y; var vz = v.z;
x = ay * vz - az * vy;
y = az * vx - ax * vz;
z = ax * vy - ay * vx;
return cast this;
}
public inline function crossvecs(a:HotVec4, b:HotVec4):HotVec4 {
var ax = a.x; var ay = a.y; var az = a.z;
var bx = b.x; var by = b.y; var bz = b.z;
x = ay * bz - az * by;
y = az * bx - ax * bz;
z = ax * by - ay * bx;
return cast this;
}
inline public function addf(x:FastFloat, y:FastFloat, z:FastFloat):Vec4 {
set_x(get_x() + x);
set_y(get_y() + y);
set_z(get_z() + z);
return cast this;
}
public inline function addvecs(a:HotVec4, b:HotVec4):HotVec4 {
x = a.x + b.x;
y = a.y + b.y;
z = a.z + b.z;
return cast this;
}
public inline function subvecs(a:HotVec4, b:HotVec4):HotVec4 {
x = a.x - b.x;
y = a.y - b.y;
z = a.z - b.z;
return cast this;
}
public inline function lerp(from:HotVec4, to:HotVec4, s:FastFloat):HotVec4 {
x = from.x + (to.x - from.x) * s;
y = from.y + (to.y - from.y) * s;
z = from.z + (to.z - from.z) * s;
return cast this;
}
public inline function applyproj(m:Mat4):HotVec4 {
var x = get_x(); var y = get_y(); var z = get_z();
var d = 1.0 / (m._03 * x + m._13 * y + m._23 * z + m._33); // Perspective divide
set_x((m._00 * x + m._10 * y + m._20 * z + m._30) * d);
set_y((m._01 * x + m._11 * y + m._21 * z + m._31) * d);
set_z((m._02 * x + m._12 * y + m._22 * z + m._32) * d);
return cast this;
}
public inline function applymat(m:Mat4):HotVec4 {
var x = get_x(); var y = get_y(); var z = get_z();
set_x(m._00 * x + m._10 * y + m._20 * z + m._30);
set_y(m._01 * x + m._11 * y + m._21 * z + m._31);
set_z(m._02 * x + m._12 * y + m._22 * z + m._32);
return cast this;
}
public inline function applymat4(m:Mat4):HotVec4 {
var x = get_x(); var y = get_y(); var z = get_z(); var w = get_w();
set_x(m._00 * x + m._10 * y + m._20 * z + m._30 * w);
set_y(m._01 * x + m._11 * y + m._21 * z + m._31 * w);
set_z(m._02 * x + m._12 * y + m._22 * z + m._32 * w);
set_w(m._03 * x + m._13 * y + m._23 * z + m._33 * w);
return cast this;
}
public inline function applyAxisAngle(axis:Vec4, angle:FastFloat):HotVec4 {
var quat = new Quat();
quat.fromAxisAngle(axis, angle);
return applyQuat(quat);
}
public inline function applyQuat(q:Quat):HotVec4 {
var ix = q.w * x + q.y * z - q.z * y;
var iy = q.w * y + q.z * x - q.x * z;
var iz = q.w * z + q.x * y - q.y * x;
var iw = -q.x * x - q.y * y - q.z * z;
x = ix * q.w + iw * -q.x + iy * -q.z - iz * -q.y;
y = iy * q.w + iw * -q.y + iz * -q.x - ix * -q.z;
z = iz * q.w + iw * -q.z + ix * -q.y - iy * -q.x;
return cast this;
}
public inline function almostEquals(v:HotVec4, prec:FastFloat):Bool {
return Math.abs(x - v.x) < prec && Math.abs(y - v.y) < prec && Math.abs(z - v.z) < prec;
}
public static inline function distance(v1:HotVec4, v2:HotVec4):FastFloat {
return distancef(v1.x, v1.y, v1.z, v2.x, v2.y, v2.z);
}
public static inline function distancef(v1x:FastFloat, v1y:FastFloat, v1z:FastFloat, v2x:FastFloat, v2y:FastFloat, v2z:FastFloat):FastFloat {
var vx = v1x - v2x;
var vy = v1y - v2y;
var vz = v1z - v2z;
return Math.sqrt(vx * vx + vy * vy + vz * vz);
}
public inline function distanceTo(p:HotVec4):FastFloat {
return Math.sqrt((p.x - x) * (p.x - x) + (p.y - y) * (p.y - y) + (p.z - z) * (p.z - z));
}
public inline function reflect(n:HotVec4):HotVec4 {
var d = 2 * dot(n);
x = x - d * n.x;
y = y - d * n.y;
z = z - d * n.z;
return cast this;
}
public inline function clamp(min:FastFloat, max:FastFloat):HotVec4 {
var l = length;
if (l < min) normalize().mult(min);
else if (l > max) normalize().mult(max);
return cast this;
}
public static inline function xAxis():HotVec4 { return new HotVec4(1.0, 0.0, 0.0); }
public static inline function yAxis():HotVec4 { return new HotVec4(0.0, 1.0, 0.0); }
public static inline function zAxis():HotVec4 { return new HotVec4(0.0, 0.0, 1.0); }
public function toString():String {
return "(" + x + ", " + y + ", " + z + ", " + w + ")";
}
}

Benchmark Results

Javascript Benchmark using Krom

These benchmarks were run using Krom.

Using Class/Struct Backend for HotVec4 Abstract ( not using hotmem )

arm.BenchmarkVec4:85: ----START BENCHMARK: Massive allocation-----
arm.BenchmarkVec4:91: Test Vec4
arm.BenchmarkVec4:94: Creating vectors...
arm.BenchmarkVec4:103: That took: 0.24351716041564941
arm.BenchmarkVec4:107: Test HotVec
arm.BenchmarkVec4:110: Creating vectors...
arm.BenchmarkVec4:119: That took: 0.20617985725402832
arm.BenchmarkVec4:122: vec4/hotvec ratio: 1.181090935161619
arm.BenchmarkVec4:127: ----START BENCHMARK: Mutable VS Immutable-----
arm.BenchmarkVec4:135: Test mutable operations
arm.BenchmarkVec4:148: That took: 6.095072031021118
arm.BenchmarkVec4:154: Test immutable operations
arm.BenchmarkVec4:167: That took: 11.535352945327758
arm.BenchmarkVec4:170: mutable/immutable ratio: 0.5283819281394286

That runs while using 225M peak of RAM and 100% of a CPU ( 1 of 4 cores )during the script run.

Using hotmem backend for HotVec4

arm.BenchmarkVec4:85: ----START BENCHMARK: Massive allocation-----
arm.BenchmarkVec4:91: Test Vec4
arm.BenchmarkVec4:94: Creating vectors...
arm.BenchmarkVec4:103: That took: 0.222977876663208
arm.BenchmarkVec4:107: Test HotVec
arm.BenchmarkVec4:110: Creating vectors...

And it hangs indefinitely at that point while climbing up to 380M of RAM and using 100% of a CPU ( 1 of 4 cores ).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment