Hi,
I will try to explain clearly my performence issue.
The following code work great and result is as expected.
Idea is to store on my RTree position and pointer of the
WorldObject.
I can so use RTree for spacial queries and do required check on
element before returning the result.
My RTree is filled by approximatly 50k elements.
The queries by the core have to be done 100k times every 100ms
(about 2 times per object with differents check)
I should mention that my cpu is not obsolete with 4core/8T at
3.6Ghz.
So i used that code for testing it.
It look like that nearest predicate is pretty slow on my system.
I got the min return time between 600-700ms after 100k queries.
Do not consider the check in the loop time as the result of
within and nearest predicate contain never more than 20 objects
and check is pretty fast.
Is that normal? Maybe i made some mistake or there is something
that i can do to optimize that.
I would love to have your opinion.
So as a start the compiler optimization should be enabled, but you
know that for sure.
There are also many assertions in the R-tree so you could disable
them by defining NDEBUG.
typedef boost::geometry::model::point<float,
3, boost::geometry::cs::cartesian> Point3D;
typedef std::pair<Point3D, WorldObject*> RTreeElement;
// define custom equal comparison is needed for pointer type
struct WObjEqualTo
{
bool operator()(RTreeElement const& v1, RTreeElement
const& v2) const
{
return boost::geometry::equals(v1.first, v2.first)
&& v1.second == v2.second;
}
};
// finally we can define our RTree type
typedef boost::geometry::index::rtree< RTreeElement,
boost::geometry::index::quadratic<16>,
boost::geometry::index::indexable<RTreeElement>,
WObjEqualTo > WorldRTree;
You could experiment with the balancing algorithm, rstar should
generate a better tree for querying. You could also play with the
Max number of elements and set it to something lower than 16, so
e.g. rtree<4>. Setting lower value could speed up balancing
algorithm in cases when there was no overlap between objects but
slow down querying if there was overlap. And if it was possible you
should pass all of the points in the constructor of the R-tree in
order to use packing algorithm.
// method to do queries
template<typename CheckType, class ObjectType>
void Map::GetNearObject(ObjectType& resultObj,
CheckType& check, Point3D centerPoint, float radius,
TypeMask typeMask /*= TYPEMASK_WORLDOBJECT*/) const
{
namespace bg = boost::geometry;
namespace bgi = boost::geometry::index;
typedef bg::model::box<Point3D> box;
box visibleBox(Point3D(centerPoint.get<0>() -
radius, centerPoint.get<1>() - radius,
centerPoint.get<2>() - radius),
Point3D(centerPoint.get<0>() + radius,
centerPoint.get<1>() + radius, centerPoint.get<2>()
+ radius));
for (WorldRTree::const_query_iterator it =
m_worldRtree.qbegin(bgi::within(visibleBox) &&
bgi::nearest(centerPoint, 1000)); it != m_worldRtree.qend();
++it)
Ok I'm slightly confused, k in nearest predicate is pretty big
(1000) and you mentioned above that there should be max 20 objects
there so my guess is that you want to get all of them. From the code
I see that you want to get only 1 closest object meeting some
additional criteria (condition below). So I'm not sure what is the
endgoal. I'm guessing that in a game probably all of the objects
within some effective range should be handled so I'll start from
that.
1. Assuming you want to handle all of the objects within a range.
This would not be knn query, it'd be spatial query because you'd
want to get all of the objects, not some number of the closest ones.
So:
- nearest predicate woudln't be needed at all
- the additional checks could be done at the querying stage, even
before the object is returned from the R-tree by using
bgi::satisfies() predicate
- the squared distance could be used to avoid square root
So using the conditions below it could look like this (in C++03
because you seem to use it):
struct MyPred
{
MyPred(Point3D const& p, float r, TypeMask m) : mask(m),
radius(r), point(p) {}
template <typename ValueType>
bool operator(ValueType const& v) const
{
// or replace distance check with squared distance check
// e.g. CARTESIAN ONLY! bg::comparable_distance(v.first,
point) < radius*radius
// and of course faster checks should be done first
// e.g. if check() was faster than distance it
should be done earlier
return v.second->isType(mask) &&
bg::distance(v.first, point) < radius &&
check(v.second);
}
private:
TypeMask mask;
float radius;
Point3D point; // or const& if you know that MyPred object
will always be temporary object
};
// ...
// iterator iterating through all objects of type defined
by typeMask in radius around centerPoint
// and this is iterator since you might want to pause the
query at some point, until all of the objects are found
m_worldRtree.qbegin(bgi::within(visibleBox)
&& bgi::satisfies(MyPred(centerPoint,
radius, typeMask))
2. In case you wanted to return only one closest object meeting the
criteria then this is knn query with k = 1 and query iterator is not
needed for that. Instead you could use query() method, but then
indeed additional within() predicate should be passed to not search
the whole tree, so:
std::vector<RTreeElement> results;
size_t found_count = rtree.query(bgi::within(visibleBox)
&& bgi::nearest(centerPoint,
1)
&&
bgi::satisfies(MyPred(centerPoint, radius, typeMask)),
std::back_inserter(results));
or less safer, passing a pointer as OutputIterator because you know
that max 1 element will be returned:
RTreeElement result;
size_t found_count = rtree.query(bgi::within(visibleBox)
&& bgi::nearest(centerPoint,
1)
&&
bgi::satisfies(MyPred(centerPoint, radius, typeMask)),
&result);
though I'm not sure if this will speed things up dramatically.
Maybe if you described a bigger picture, what you're trying to do in
general, etc. it could be possible to find a better solution. E.g.
why are you creating the query iterators each time when they could
be stored, the query could be paused and resumed after doing
something else. Otherwise there is no need to use them, because
query() method is sufficient.
{
if (it->second->isType(typeMask) &&
bg::distance(it->first, centerPoint) < radius &&
check(it->second))
{
resultObj = static_cast<ObjectType>(it->second);
break;
}
}
};
<snip>
Adam