Working on a vector math experiment where I try to minimize garbage collection (so avoiding creation of new instances) while keeping the math very readable. I am wondering: what is the most convenient way to determine the type of an identity inside a macro.
In the example below, inside the Macro.Vectorize function, can you find out what the compiler thinks will be the type of “position” or “forward”?
class Node
{
private var position : Vector3;
private var forward : Vector3;
private function MoveForward()
{
position = Macro.Vectorize(position + forward);
}
}
I recommend you have a look at inline constructors and operator overloading with abtract classes, with them you can implement inline vector math without the use of macros (only works in Haxe 4.0.0 though)
Thanks for taking the effort to respond @basro.
The thing is that this approach did not fully give me what I wanted last time I tried. The main reason for this is that an abstract class needs to “inherit” from another type. What I want here is avoiding the creation of a new instance. So in fact when doing vector math I would like a Vector3 to be 3 individual float and not 3 float wrapped inside an object.
In the example above I want the code to compile to:
The value of what I am trying to achieve is not really shown very well in this sample. Especially when you need to perform dot and cross products inside a function (that involves temporary vectors) this would avoid the construction of new instances.
Hope this makes sense. If I have missed anything please let me know!
@remcohuijser, yes I had understand, that’s what the inline constructors part would achieve. I’ve been using this combo in my own projects to achieve ergonomic and efficient vector math that doesn’t create new instances (unless needed).
I’ve made a small example here: http://try-haxe.mrcdk.com/#35725
Make sure to set the compiler in the options tab to use Haxe 4, check the output to see that there’s no new instances being allocated.
Regarding assignment of position in your example, in my code I’ve solved that by implementing a set method.
I’m not sure if it’s possible to override the = as an operator using abstracts but it’s probably a bad idea for the cognitive load it would produce.
Test.MoveForward = function() {
var this1 = Test.position;
var v0 = Test.position;
var v1 = Test.forward;
this1.x = v0.x + v1.x;
this1.y = v0.y + v1.y;
};
I believe the compiler optimizer has improved since preview1 and the output would be smaller if you use the latest haxe 4 compiler. Edit: This was wrong.
This should still produce the same code. It’s hard to gauge if a temporary variable is better or worse than two field access operations.
By the way, that try-haxe allows you to pick a recent development version, too. I don’t know why of all our preview releases it only shows preview.1 though.
I knew try-haxe was there but I am not using it often enough: I usually create a new spike project for these kinds of things. Very nice approach to quickly try something.
The result you showed looks very promising! I was wondering why I got such a different result last time. The main difference is that instead of making Vec2Class a class I used a typedef.
var a = Vec2.New(1,1);
var b = Vec2.New(1,2);
trace( (a + b).length() );
Haxe 3.4.4 with typedef
var this1 = { x : 1 + 1, y : 1 + 2};
console.log(Math.sqrt(this1.x * this1.x + this1.y * this1.y));
Haxe 3.4.4 with class
var a = new Vec2Class(1,1);
var b = new Vec2Class(1,2);
var this1 = new Vec2Class(a.x + b.x,a.y + b.y);
console.log(Math.sqrt(this1.x * this1.x + this1.y * this1.y));
Haxe 4.0.0 p1 with typedef
var a = { x : 1, y : 1};
var b = { x : 1, y : 2};
var this_x = a.x + b.x;
var this_y = a.y + b.y;
console.log(“Test.hx:33:”,Math.sqrt(this_x * this_x + this_y * this_y));
Haxe 4.0.0 p1 with class
var x = 1 + 1;
var y = 1 + 2;
console.log("Test.hx:37:",Math.sqrt(x * x + y * y));
To me the last one looks most promising: no object creation and very short code. @Simn can you explain the different for Haxe 4.0.0 (I will not use 3.4.4) and is this something you would expect to happen?
Maybe nice to know I did a similar thing to create a vector2 class hx-vector2d (0.0.2) Which is also an abstract, which is nice because
make instances out of points from other libraries.
allow operator overloading. example I can do var point3 = point1 + point2 which translates to var point3 = {x: point1.x + point2.x, y: point1.y + point2.y}.
If Haxe compiler can optimize, there are not even instances but it translates to just floats.
it allows to be constructed it in several ways:
var pointA:Vector2d = {x: 2.0, y: 1.5};
var pointB:Vector2d = new Vector2d(2.0, 1.5);
@remcohuijser, I’m used to adding @:extern to inline methods I know I only want to use as inline because it prevents the compiler from generating the concrete methods in the output. The compiler will remove them through DCE anyway so it’s not important.
I don’t fully understand why this is happening but you can make the typedef version “work” by changing the arguments of New into float literals. (like this http://try-haxe.mrcdk.com/#d2909)
I believe the inlining is being cancelled because x and y are seen as Int first but later seen as Float. I don’t understand why that would be the case, why is it not seeing them as floats all the time. Smells like a small bug somewhere.