Value types vs allocated types and SoA in Haxe

I am sure this must be properly addressed somewhere, but I just cannot find where.

I am starting to experiment with Haxe and one of the things about which I have not been able to find clarity is whether it is possible to create “value” types. By this I mean a type that is created on the stack and that is generally passed around by copy like an integer or a float, as opposed to being allocated on the heap and passed around by reference. Importantly, a collection of a value type (e.g. an array) would store the values themselves, not references to the objects. A typical example would be things like Vector2 or Vector3 in video games. I thought that anonymous structures might be something like that, but according to the manual their performance is actually worse than for classes, so I suppose they are implemented as a sort of dictionary on static targets.

My impression is that this is not possible in Haxe, but I have not found explicit documentation about it. I know there are inline constructors that do something like that, but I’m not sure it is quite that (e.g. what happens if I have an array of a type with an inline constructor?).

I suspect this is partially, or totally, due to the multi-target nature of the language, so things might in principle work differently on different targets. But it seems a relatively important aspect. For example, there is this idea of Structure of Arrays (SoA) that some games or engines implement to gain some performance from better cache locality (it is frequently associated with ECS designs, although one doesn’t necessarily imply the other). But this only makes sense if you can store value types in your arrays, not references. So, is it possible at all to have a SoA design in Haxe (for anything else than boolean, integer or float values)?

Value types are not supported in haxe at the moment (many of Haxe’s targets do not support them: JS, Java, Python, Lua, etc.)
When targetting C# there is the @:struct meta to generate C# structs.
Value types could also be implemented for hashlink or hxcpp, but are not at the moment.
(They might already be possible on hxcpp, but I’m not sure.)

1 Like

@:struct also works for hashlink.

2 Likes

Something I wonder if we could explore in static targets would be marking variables that escape to an outer scope, any variable that doesn’t can be stack allocated and doesn’t need to touch the GC (giving you the perf benefits of value types, hopefully faster since no copying is required)

This wouldn’t help with SoAs, for those you could use an abstract to make a struct over some arbitrary byte buffer, that way you know exactly where your memory is and how it’s arrange

@RealyUniqueName when the analyser runs does it already determine function purity somewhere?

@:struct on hashlink will not generate a pass-by-value struct.
Simply remove the extra hl_type *t field from classes.

@javidcf to expand on implementing SoA and AoS with abstracts over plain byte buffers I’ve created a macro that does this for you, given a structure

Demo: Try Haxe !

For example, to create a SoA object pool you can do:

typedef Player = ObjectPool.SoA<'Player', {
	x: Float,
	y: Float,
	age: Int,
	eyeColor: EyeColor,
	alive: Bool,
	velocity: {
		x: Float,
		y: Float,
		q: {a: Int, b: Int},
	},
}>;

You can create instances with new Player(). This returns simply and Int, corresponding to the index in the SoA arrays

In AoS mode, nested structures are flattened so you have just one big chunk of bytes corresponding to each instance

Implementation is here:

7 Likes

Really cool! Thanks, George!

1 Like

Yeah, George, really impressive example👍! It would be nice to have it in the haxe cookbook:)

1 Like