C++ 八叉树:我做错了吗?插入速度非常慢

C++ 八叉树:我做错了吗?插入速度非常慢,c++,memory-management,octree,C++,Memory Management,Octree,我正在将一个基于八叉树的容器从10点到10亿点写入内存。。由于加载的数据量很大,我需要注意内存消耗 一切似乎都正常工作,并根据需要进行分段,但插入时间非常缓慢。可能是因为数据在父对象到子对象之间的重新分配。 我能做些什么来优化它吗?我是否正确地实施了这一点? 我可以在每个节点中保留向量以包含最大数量的点,但这会显著增加所需的内存 使用一个简单的R树型容器,我在48秒内加载了4.68亿个点。。 使用下面的八叉树,我在245秒内加载 class OctreeNode { public

我正在将一个基于八叉树的容器从10点到10亿点写入内存。。由于加载的数据量很大,我需要注意内存消耗

一切似乎都正常工作,并根据需要进行分段,但插入时间非常缓慢。可能是因为数据在父对象到子对象之间的重新分配。 我能做些什么来优化它吗?我是否正确地实施了这一点? 我可以在每个节点中保留向量以包含最大数量的点,但这会显著增加所需的内存

使用一个简单的R树型容器,我在48秒内加载了4.68亿个点。。 使用下面的八叉树,我在245秒内加载

    class OctreeNode {
    public:
        std::vector<std::shared_ptr<OctreeNode>>    Children;
        std::vector<TPoint> Data;
        BoundingBox         Bounds;

        OctreeNode(){}

        OctreeNode(BoundingBox bounds) : Bounds(bounds){
        }

        ~OctreeNode(void){}

        void Split();

    };

    typedef std::shared_ptr<OctreeNode> OctreeNodePtr;


    void OctreeNode::Split()
    {
        Point box[8];
        Bounds.Get8Corners(box);
        Point center = Bounds.Center;

        Children.reserve(8);
        Children.push_back(OctreeNodePtr(new OctreeNode(BoundingBox::From(box[0], center))));
        Children.push_back(OctreeNodePtr(new OctreeNode(BoundingBox::From(box[1], center))));
        Children.push_back(OctreeNodePtr(new OctreeNode(BoundingBox::From(box[3], center))));
        Children.push_back(OctreeNodePtr(new OctreeNode(BoundingBox::From(box[2], center))));


        Children.push_back(OctreeNodePtr(new OctreeNode(BoundingBox::From(box[5], center))));
        Children.push_back(OctreeNodePtr(new OctreeNode(BoundingBox::From(box[4], center))));
        Children.push_back(OctreeNodePtr(new OctreeNode(BoundingBox::From(box[6], center))));
        Children.push_back(OctreeNodePtr(new OctreeNode(BoundingBox::From(box[7], center))));
    }



    Octree::Octree(BoundingBox bounds) : Bounds(bounds)
    {
        _root = OctreeNodePtr(new OctreeNode(bounds));
        _root->Split();
    }


    Octree::~Octree()
    {
    }



    bool Octree::InsertPoint(TPoint &p)
    {
        return InsertPoint(p, _root);
    }

    bool Octree::InsertPoint(TPoint &p, const OctreeNodePtr &parent)
    {
        if (parent->Children.size() != 0){
            for (size_t i = 0; i < parent->Children.size(); i++){
                OctreeNodePtr &currentNode = parent->Children[i];
                if (currentNode->Bounds.IsContained(p.ToPoint3d())){
                    return InsertPoint(p, currentNode);
                }           
            }

            // Was not able to insert a point.
            return false;
        }

        BoundingBox newBounds = parent->Bounds;
        newBounds.Extend(p.ToPoint3d());


        // Check for split condition...
        if (parent->Data.size() == MaxPerNode && newBounds.XLength() > 0.01){

            // Split it...thus generating children nodes
            parent->Split();


            // Resize the children arrays so that we don't have to keep allocating when redistributing points..
            for (size_t i = 0; i < parent->Children.size(); i++){
                parent->Children[i]->Data.reserve(parent->Data.size());
            }


            // Distribute the points that were in the parent to its children..
            for (size_t i = 0; i < parent->Data.size(); i++){
                TPoint originalPoint = parent->Data[i];
                if (!InsertPoint(originalPoint, parent)){
                    printf("Failed to insert point\n");
                    break;
                }
            }

            // Insert the current point.
            if (!InsertPoint(p, parent)){
                printf("Failed to insert point\n");
            }


            // Resize the arrays back so it fits the size of the data.....
            for (size_t i = 0; i < parent->Children.size(); i++){
                parent->Children[i]->Data.shrink_to_fit();
            }

            // clear out the parent information
            parent->Data.clear();
            parent->Data.shrink_to_fit();
            return true;
        } else {
            // Node is valid so insert the data..
            if (parent->Data.size() <= 100000){
                parent->Data.push_back(p);
            } else {
                printf("Too much data in tiny node... Stop adding\n");
            }

            return true;
        }


    }


    void Octree::Compress(){
        Compress(_root);
    }

    void Octree::Compress(const OctreeNodePtr &parent){


        if (parent->Children.size() > 0){

            // Look for and remove useless cells who do not contain children or point cloud data.
            size_t j = 0;
            bool removed = false;
            while (j < parent->Children.size()){
                if (parent->Children[j]->Children.size() == 0 && parent->Children[j]->Data.size() == 0){
                    parent->Children.erase(parent->Children.begin() + j);
                    removed = true;
                } else {
                    Compress(parent->Children[j]);
                    ++j;
                }
            }

            if (removed)
                parent->Children.shrink_to_fit();

            return;
        }

        parent->Data.shrink_to_fit();
    }
类八进制{ 公众: 性病媒儿童; std::矢量数据; 边界框边界; 八叉树节点(){} 八叉树节点(边界框边界):边界(边界){ } ~OctreeNode(void){} 无效分割(); }; typedef std::shared_ptr OctreeNodePtr; void OctreeNode::Split() { 积分箱[8]; 边界。获取8个角(框); 点中心=边界。中心; 儿童保护区(8); 向后推(OctreeNodePtr(新的OctreeNode(BoundingBox::From)(框[0],中心)); 向后推(OctreeNodePtr(新的OctreeNode(BoundingBox::From)(框[1],中心)); 向后推(OctreeNodePtr(新的OctreeNode(BoundingBox::From)(框[3],中间)); 向后推(OctreeNodePtr(新的OctreeNode(BoundingBox::From)(框[2],中间)); 向后推(OctreeNodePtr(新的OctreeNode(BoundingBox::From)(方框[5],中间)); 向后推(OctreeNodePtr(新的OctreeNode(BoundingBox::From)(方框[4],中间)); 向后推(OctreeNodePtr(新的OctreeNode(BoundingBox::From)(方框[6],中间)); 向后推(OctreeNodePtr(新的OctreeNode(BoundingBox::From)(方框[7],中间)); } 八叉树::八叉树(边界框边界):边界(边界) { _root=OctreeNodePtr(新的OctreeNode(边界)); _root->Split(); } 八叉树::~Octree() { } 布尔八叉树::插入点(TPoint&p) { 返回插入点(p,_根); } bool八叉树::InsertPoint(TPoint&p,const-OctreeNodePtr&parent) { 如果(父级->子级.size()!=0){ 对于(size_t i=0;iChildren.size();i++){ OctreeNodePtr¤tNode=parent->Children[i]; 如果(currentNode->Bounds.IsContained(p.ToPoint3d())){ 返回InsertPoint(p,currentNode); } } //无法插入点。 返回false; } BoundingBox新边界=父级->边界; 扩展(p.ToPoint3d()); //检查拆分条件。。。 如果(父级->数据.size()==MaxPerNode&&newBounds.XLength()>0.01){ //拆分它…从而生成子节点 父->拆分(); //调整子数组的大小,以便在重新分配点时不必继续分配。。 对于(size_t i=0;iChildren.size();i++){ parent->Children[i]->Data.reserve(parent->Data.size()); } //将父对象中的点分发给其子对象。。 对于(size_t i=0;iData.size();i++){ TPoint originalPoint=parent->Data[i]; 如果(!InsertPoint(原始点,父项)){ printf(“插入点失败\n”); 打破 } } //插入当前点。 如果(!InsertPoint(p,父级)){ printf(“插入点失败\n”); } //重新调整阵列的大小,使其适合数据的大小。。。。。 对于(size_t i=0;iChildren.size();i++){ 父级->子级[i]->Data.shrink_to_fit(); } //清除家长信息 父->数据.clear(); 父项->数据。将_收缩到_拟合(); 返回true; }否则{ //节点有效,因此请插入数据。。 if(parent->Data.size()Data.push_back(p); }否则{ printf(“微小节点中的数据太多…停止添加\n”); } 返回true; } } void八叉树::Compress(){ 压缩(_根); } void八叉树::压缩(常量八叉树和父项){ 如果(父级->子级.size()>0){ //查找并删除不包含子元素或点云数据的无用单元。 尺寸j=0; bool-removed=false; 而(jChildren.size()){ 如果(父级->子级[j]->Children.size()==0&&parent->Children[j]->Data.size()==0){ 父->子.erase(父->子.begin()+j); 删除=真; }否则{ 压缩(父级->子级[j]); ++j; } } 如果(已删除) 父级->子级。收缩到适合(); 返回; } 父项->数据。将_收缩到_拟合(); }
只是一件小事,但要取代它:

Children.push_back(OctreeNodePtr(new OctreeNode(BoundingBox::From(box[0], center))));
为此:

Children.push_back(std::make_shared<OctreeNode>(BoundingBox::From(box[0], center)));
Children.push_back(std::make_shared(BoundingBox::From(box[0],center));
将稍微减少加载时间并减少内存消耗


这对于任何共享ptr都是正确的。make_shared route将控制块与共享对象合并。

我在这里看到的是,要插入点,您需要遍历8个子对象,并检查每个子对象是否在内部

八叉树的主要优点是,根据边界框中心和数据的位置,可以