为什么boost.geometry.index.rtree比superliminal.RTree慢

Why boost.geometry.index.rtree is slower than superliminal.RTree

本文关键字:superliminal RTree rtree boost geometry index 为什么      更新时间:2023-10-16

我测试了boost.geometry.index.rtree (boost 1.59 www.boost.org)和superliminal。RTree (http://superliminal.com/sources/sources.htm # C_Code)。

令我惊讶的是,超级阈值。RTree比boost.geometry.index.rtree更快。

环境设置
  • 在阈值中添加相同的空间索引数据。RTree和boost.geometry.index.rtree对象。
  • 测试相同的空间索引查询100次,得到所消耗的时间。
  • GCC版本为"GCC version 4.4.6 20110731 (Red Hat 4.4.6-4) (GCC)",使用"-g -o2 -Wall -finline-functions"编译器标志。
  • 使用RTree <uint64_t,>

提高代码
namespace bg = boost::geometry; 
namespace bgi = boost::geometry::index; 
typedef bg::model::point < int, 2, bg::cs::cartesian > point_t; 
typedef bg::model::box < point_t > box_t; 
typedef std::pair < box_t, uint64_t > value_t; 
typedef bgi::rtree < value_t, bgi::quadratic < 8, 4 > > rtree_t; 

结果是:

superliminal。RTree 0.029 s

boost.geometry.index。rtree 0.12 s。

很难说明为什么在您的情况下它较慢,因为您没有共享代码,也没有告诉您如何确切地使用r树实现。您也没有提供有关您正在存储的数据的任何信息。当你比较算法或数据结构时,这些东西是非常重要的。

我只能猜测你使用Superliminar R-tree的方式与它在附加的测试文件中使用的方式相同,所以你将回调函数传递到Search成员函数。我猜你用的是Boost。几何r树的方式与快速开始部分中的文档中显示的相同,因此您将std::back_insert_iterator的对象传递到query成员函数中。我们来看看这个

#include "RTree.h"
#include <vector>
#include <boost/geometry.hpp>
#include <boost/geometry/index/rtree.hpp>
#include <boost/timer.hpp>
struct Rect
{
  Rect()  {}
  Rect(int a_minX, int a_minY, int a_maxX, int a_maxY)
  {
    min[0] = a_minX;
    min[1] = a_minY;
    max[0] = a_maxX;
    max[1] = a_maxY;
  }
  int min[2];
  int max[2];
};
// used with Superliminar R-tree
std::vector<uint64_t> res;
bool MySearchCallback(uint64_t id, void* arg)
{
    res.push_back(id);
    return true;
}
int main()
{
    // randomize rectangles
    std::vector<Rect> rects;
    for (size_t i = 0 ; i < 300000 ; ++i)
    {
        int min_x = rand() % 10000;
        int min_y = rand() % 10000;
        int w = 1 + rand() % 100;
        int h = 1 + rand() % 100;
        rects.push_back(Rect(min_x, min_y, min_x+w, min_y+h));
    }
    // create the rectangle passed into the query
    Rect search_rect(4000, 4000, 6000, 6000);
    // create the Superliminar R-tree
    RTree<uint64_t, int, 2, int64_t> tree;
    // create the Boost.Geometry R-tree
    namespace bg = boost::geometry;
    namespace bgi = boost::geometry::index;
    typedef bg::model::point<int, 2, bg::cs::cartesian> point_t;
    typedef bg::model::box<point_t> box_t;
    typedef std::pair<box_t, uint64_t> value_t;
    bgi::rtree<value_t, bgi::quadratic<8, 4> > bg_tree;
    // Insert values
    for(size_t i = 0; i < rects.size(); i++)
    {
        Rect const& r = rects[i];
        tree.Insert(r.min, r.max, i);
        box_t b(point_t(r.min[0], r.min[1]), point_t(r.max[0], r.max[1]));
        bg_tree.insert(value_t(b, i));
    }
    // test Rtree
    {
        int sum = 0;
        boost::timer t;
        for (size_t i = 0 ; i < 100 ; ++i)
        {
            res.clear();
            sum += tree.Search(search_rect.min, search_rect.max, MySearchCallback, NULL);
        }
        double s = t.elapsed();
        std::cout << s << " " << sum << std::endl;
    }
    // test BG Rtree
    {
        box_t search_box(
            point_t(search_rect.min[0], search_rect.min[1]),
            point_t(search_rect.max[0], search_rect.max[1]));
        size_t sum = 0;
        boost::timer t;
        for (size_t i = 0 ; i < 100 ; ++i)
        {
            std::vector<value_t> res;
            sum += bg_tree.query(bgi::intersects(search_box), std::back_inserter(res));
        }
        double s = t.elapsed();
        std::cout << s << " " << sum << std::endl;
    }
}

结果(使用GCC 4.8 -O2 -finline-functions)如下:

0.014s for Superliminar R-tree
0.072s for Boost.Geometry R-tree

所以它们和你的相似,即一个快了5倍。注意,在这两种情况下,我都创建了一个容器来存储结果(Superliminar的id和Boost.Geometry的整数值)。问题是r树的使用方式不同。

优化1

让我们试着通过以相同的方式存储相同的结果来消除两种r树在使用上的差异。为了做到这一点,删除临时的std::vector<value_t>。要实现回调,将std::back_insert_iterator替换为一个名为boost::function_output_iterator的函数对象。在这两种情况下,只存储std::vector<uint64_t>中的id,并希望编译器会优化代码。

#include "RTree.h"
#include <vector>
#include <boost/function_output_iterator.hpp>
#include <boost/geometry.hpp>
#include <boost/geometry/index/rtree.hpp>
#include <boost/timer.hpp>
struct Rect
{
  Rect()  {}
  Rect(int a_minX, int a_minY, int a_maxX, int a_maxY)
  {
    min[0] = a_minX;
    min[1] = a_minY;
    max[0] = a_maxX;
    max[1] = a_maxY;
  }
  int min[2];
  int max[2];
};
std::vector<uint64_t> res;
// used with Superliminar R-tree
bool MySearchCallback(uint64_t id, void* arg)
{
    res.push_back(id);
    return true;
}
// used with Boost.Geometry R-tree
struct MySearchCallback2
{
    template <typename Value>
    void operator()(Value const& v)
    {
        res.push_back(v.second);
    }
};
int main()
{
    // randomize rectangles
    std::vector<Rect> rects;
    for (size_t i = 0 ; i < 300000 ; ++i)
    {
        int min_x = rand() % 10000;
        int min_y = rand() % 10000;
        int w = 1 + rand() % 100;
        int h = 1 + rand() % 100;
        rects.push_back(Rect(min_x, min_y, min_x+w, min_y+h));
    }
    // create the rectangle passed into the query
    Rect search_rect(4000, 4000, 6000, 6000);
    // create the Superliminar R-tree
    RTree<uint64_t, int, 2, int64_t> tree;
    // create the Boost.Geometry R-tree
    namespace bg = boost::geometry;
    namespace bgi = boost::geometry::index;
    typedef bg::model::point<int, 2, bg::cs::cartesian> point_t;
    typedef bg::model::box<point_t> box_t;
    typedef std::pair<box_t, uint64_t> value_t;
    bgi::rtree<value_t, bgi::quadratic<8, 4> > bg_tree;
    // Insert values
    for(size_t i = 0; i < rects.size(); i++)
    {
        Rect const& r = rects[i];
        tree.Insert(r.min, r.max, i);
        box_t b(point_t(r.min[0], r.min[1]), point_t(r.max[0], r.max[1]));
        bg_tree.insert(value_t(b, i));
    }
    // test Rtree
    {
        int sum = 0;
        boost::timer t;
        for (size_t i = 0 ; i < 100 ; ++i)
        {
            res.clear();
            sum += tree.Search(search_rect.min, search_rect.max, MySearchCallback, NULL);
        }
        double s = t.elapsed();
        std::cout << s << " " << sum << std::endl;
    }
    // test BG Rtree
    {
        box_t search_box(
            point_t(search_rect.min[0], search_rect.min[1]),
            point_t(search_rect.max[0], search_rect.max[1]));
        size_t sum = 0;
        MySearchCallback2 callback;
        boost::timer t;
        for (size_t i = 0 ; i < 100 ; ++i)
        {
            res.clear();
            sum += bg_tree.query(bgi::intersects(search_box), boost::make_function_output_iterator(callback));
        }
        double s = t.elapsed();
        std::cout << s << " " << sum << std::endl;
    }
}

在这种情况下,结果是:

0.014s for Superliminar R-tree
0.033s for Boost.Geometry R-tree
优化2

可以做的另一件事是禁用断言。助推器里有一些。几何r - tree。在用-DNDEBUG编译代码之后,结果更加接近了:

0.014s for Superliminar R-tree
0.015s for Boost.Geometry R-tree
结论

在这个综合测试用例中,对于随机数据等,结果大致相同。同样,对你来说,它们可能是不同的,我不知道你到底在做什么,所以我不能告诉你是什么问题。

这是一个非常简单的用例,如果测试更复杂的用例,结果可能会有所不同。换句话说,应该分析一个实际的应用程序,看看是否存在一些瓶颈。

进一步提振。几何R-tree的代码要复杂得多。两个r树实现的接口是不同的,特别是两个搜索/查询函数需要不同的参数,并且处理它们的方式也不同。编译器可以选择以不同的方式优化代码。

注:

与提升。几何r树可以使用不同的分割算法和打包算法,这样你就可以试验哪种算法最适合你的情况。要使用打包算法,您必须将一系列值传递给rtree构造函数。对于使用打包算法创建的相同数据和元素数量和rtree,查询时间为0.005s