I am implementing a particle-based fluid simulation. To represent vectors such as velocity, acceleration etc I have defined a class that looks like this
class Vec3f {
public:
float x, y, z;
// ... bunch of constructors, operators and utility functions
}
I'm using the library nanoflann for kd-tree searches. To accommodate for arbitrary class designs, nanoflann requires a user-defined adaptor class that the kd-tree class then queries to get info about the particle dataset. One function that the adaptor has to offer, as described in the nanoflann documentation is the following.
// Must return the dim'th component of the idx'th point in the class:
inline T kdtree_get_pt(const size_t idx, int dim) const { ... }
The problem is that this interface does not work seamlessly with the x, y, z
representation. Naively, it would need to do something like this
inline float kdtree_get_pt(const size_t idx, int dim) const {
switch(dim) {
case 0:
return particlearray[idx].x;
case 1:
return particlearray[idx].y;
case 2:
return particlearray[idx].z;
}
}
Building and querying the kd-tree consumes a significant portion of my app's runtime and kd_tree_get_pt
gets queried multiple times in the process so I need it to be optimized. The following solution should be faster.
class Vec3f {
public:
float values[3];
// ...
}
// Then inside the adaptor class
inline float kdtree_get_pt(const size_t idx, int dim) const {
return particlearrray[idx].values[dim];
}
However, I much prefer the x, y, z
interface for my equations. Now that the problem is clear, my question is how can I keep the x, y, z
notation for my equations without making kdtree_get_pt
suboptimal.
Solutions I have considered:
Vec3f
has memberfloat values[3]
and getters in the form offloat& x()
. The function call should be optimized away completely so this almost works but I do not want to have to add the parentheses in my equations if I can avoid it. eg I want to be able to writevec1.x - vec2.x
instead ofvec1.x() - vec2.x()
. As far as I know, C++ does not offer a way to "disguise" a function call as a member variable, excluding preprocessor macros which I do not consider a safe solution.Vec3f has members
float values[3]
andfloat& x, y, z
where the latter are initialized to point to the correspondingfloat
s in the array. I thought they would be optimized away as they are known at compile time and obviously cannot change value after initialization, but even with optimizations on, MSVC++ seems to actually store thefloat&
s as can be seen bysizeof(Vec3f)
growing by 12 bytes after their addition. This doubles the storage size of my dataset which raises a concern for cache misses when working with arbitrarily large datasets.kdtree_get_pt
usesfloat& values[3]
to point tox, y, z
. This might eliminate the branching cost, but I don't believe the extra level of indirection, nor the need to initialize all 3 references can be optimized away so it is presumably slower than the return particlearrray[idx][dim]` version.kdtree_get_pt
usesreinterpret_cast
or pointer magic to directly point intoVec3f
's members. Given aVec3f
object's address, I believex, y, z
are guaranteed to be stored in that order with the first one stored at the same address as the one given by the&
operator on theVec3f
object, but even so I'm confused as to whether there exists a well-defined way to observe them.