shared_ptr的:可怕的速度shared_ptr的:可怕的速度(shared_ptr: hor

2019-05-13 07:30发布

当比较指针,与经典的两个变种的shared_ptr-I是由程序运行速度的显著上升感到惊讶。 为了测试二维Delaunay增量插入算法已被使用。

编译器设置:

VS 2010(释放)/ O2 / MD / GL,W7教授,CPU双核3.GHZ

结果:

shared_ptr的(C ++ 0×00):

N[points]         t[sec]  
100 000                6  
200 000               11  
300 000               16  
900 000               36  

指针:

N[points]         t[sec]  
100 000              0,5  
200 000               1  
300 000               2  
900 000               4   

shared_ptr的版本的运行时间越长大约10倍。 难道这引起编译器设置或C ++为0x00 shared_ptr的实现是如此之慢?

VS2010探查:对于原始指针的时间约60%是由含插入点(它被确定,这是一个众所周知的事实)的三角形的启发式搜索花费。 但对于shared_ptr的版本时约58%正在使用shared_ptr.reset(),只有10%的花费用于启发式搜索。

测试与原始指针的代码:

void DT2D::DT ( Node2DList *nl, HalfEdgesList *half_edges_dt, bool print )
{
    // Create 2D Delaunay triangulation using incremental insertion method
    unsigned int nodes_count_before = nl->size();

    // Remove duplicit points
    nl->removeDuplicitPoints();

    // Get nodes count after deletion of duplicated points
    unsigned int nodes_count_after = nl->size();

    //Print info
    std::cout << "> Starting DT, please wait... ";
    std::cout << nodes_count_after << " points, " << ( nodes_count_before - nodes_count_after ) << " removed.";

    // Are in triangulation more than three points
    try
    {
            //There are at least 3 points
            if ( nodes_count_after > 2 )
            {
                    // Create simplex triangle
                    createSimplexTriangle ( nl, half_edges_dt );

                    // Increment nodes count
                    nodes_count_after += 3;

                    // Starting half edge using for searching
                    HalfEdge *e_heuristic = ( *half_edges_dt ) [0];

                    // Insert all points into triangulation using incremental method
                    for ( unsigned int i = 3; i < nodes_count_after; i++ )  // Jump over simplex
                    {
                            DTInsertPoint ( ( *nl ) [i], &e_heuristic, half_edges_dt );
                    }

                    //Corect boundary triangles (swap edges in triangles adjacent to simplex triangles).
                    //They are legal due to DT, but not creating the convex hull )
                    correctBoundaryTriangles ( nl, half_edges_dt );

                    // Remove triangles having simplex points
                    removeSimplexTriangles ( nl, half_edges_dt );
            }

            //Print results
            std::cout << " Completed." << std::endl;
    }

插入点的过程:

void DT2D::DTInsertPoint ( Point2D *p, HalfEdge **e1, HalfEdgesList *half_edges_dt )
{
    // One step of the Delaunay triangulation, incremental insertion by de Berg (2001)
    short   status = -1;

    //Pointers
    HalfEdge *e31 = NULL;
    HalfEdge *e21 = NULL;
    HalfEdge *e12 = NULL;
    HalfEdge *e32 = NULL;
    HalfEdge *e23 = NULL;
    HalfEdge *e13 = NULL;
    HalfEdge *e53 = NULL;
    HalfEdge *e44 = NULL;
    HalfEdge *e63 = NULL;

    try
    {
            // Test, if point lies inside triangle
            *e1 = LawsonOrientedWalk::findTriangleWalk ( p, &status, *e1, 0 );

            if ( e1 != NULL )
            {
                    // Edges inside triangle lies the point
                    HalfEdge *e2 = ( *e1 )->getNextEdge();
                    HalfEdge *e3 = e2->getNextEdge();

                    // Point lies inside the triangle
                    if ( status == 1 )
                    {
                            // Create first new triangle T1, twin edges set after creation
                            e31 = new HalfEdge ( p, *e1, NULL );
                            e21 = new HalfEdge ( e2->getPoint(), e31, NULL );
                            ( *e1 )->setNextEdge ( e21 );

                            // Create second new triangle T2, twin edges set after creation
                            e12 = new HalfEdge ( p, e2, NULL );
                            e32 = new HalfEdge ( e3->getPoint(), e12, NULL );
                            e2->setNextEdge ( e32 );

                            // Create third new triangle T3, twin edges set after creation
                            e23 = new HalfEdge ( p, e3, NULL );
                            e13 = new HalfEdge ( ( *e1 )->getPoint(), e23, NULL );
                            e3->setNextEdge ( e13 );

                            // Set twin edges in T1, T2, T3
                            e12->setTwinEdge ( e21 );
                            e21->setTwinEdge ( e12 );
                            e13->setTwinEdge ( e31 );
                            e31->setTwinEdge ( e13 );
                            e23->setTwinEdge ( e32 );
                            e32->setTwinEdge ( e23 );

                            // Add new edges into list
                            half_edges_dt->push_back ( e21 );
                            half_edges_dt->push_back ( e12 );
                            half_edges_dt->push_back ( e31 );
                            half_edges_dt->push_back ( e13 );
                            half_edges_dt->push_back ( e32 );
                            half_edges_dt->push_back ( e23 );

                            // Legalize triangle T1
                            if ( ( *e1 )->getTwinEdge() != NULL )
                            {
                                    legalizeTriangle ( p, *e1 );
                            }

                            // Legalize triangle T2
                            if ( e2->getTwinEdge() != NULL )
                            {
                                    legalizeTriangle ( p, e2 );
                            }

                            // Legalize triangle T3
                            if ( e3->getTwinEdge() != NULL )
                            {
                                    legalizeTriangle ( p, e3 );
                            }
                    }

                    // Point lies on the edge of the triangle
                    else if ( status == 2 )
                    {
                            // Find adjacent triangle
                            HalfEdge *e4 = ( *e1 )->getTwinEdge();
                            HalfEdge *e5 = e4->getNextEdge();
                            HalfEdge *e6 = e5->getNextEdge();

                            // Create first new triangle T1, twin edges set after creation
                            e21 = new HalfEdge ( p, e3, NULL );
                            ( *e1 )->setNextEdge ( e21 );

                            // Create second new triangle T2, OK
                            e12 = new HalfEdge ( p, e2, e4 );
                            e32 = new HalfEdge ( e3->getPoint(), e12, e21 );
                            e2->setNextEdge ( e32 );

                            // Create third new triangle T3, twin edges set after creation
                            e53 = new HalfEdge ( p, e6, NULL );
                            e4->setNextEdge ( e53 );

                            // Create fourth new triangle T4, OK
                            e44 = new HalfEdge ( p, e5, *e1 );
                            e63 = new HalfEdge ( e6->getPoint(), e44, e53 );
                            e5->setNextEdge ( e63 );

                            // Set twin edges in T1, T3
                            e21->setTwinEdge ( e32 );
                            ( *e1 )->setTwinEdge ( e44 );
                            e53->setTwinEdge ( e63 );
                            e4->setTwinEdge ( e12 );

                            // Add new edges into list
                            half_edges_dt->push_back ( e21 );
                            half_edges_dt->push_back ( e12 );
                            half_edges_dt->push_back ( e32 );
                            half_edges_dt->push_back ( e53 );
                            half_edges_dt->push_back ( e63 );
                            half_edges_dt->push_back ( e44 );

                            // Legalize triangle T1
                            if ( e3->getTwinEdge() != NULL )
                            {
                                    legalizeTriangle ( p, e3 );
                            }

                            // Legalize triangle T4
                            if ( e5->getTwinEdge() != NULL )
                            {
                                    legalizeTriangle ( p, e5 );
                            }

                            // Legalize triangle T3
                            if ( e6->getTwinEdge() != NULL )
                            {
                                    legalizeTriangle ( p, e6 );
                            }

                            // Legalize triangle T2
                            if ( e2->getTwinEdge() != NULL )
                            {
                                    legalizeTriangle ( p, e2 );
                            }
                    }
            }
    }
    //Throw exception
    catch ( std::bad_alloc &e )
    {
            //Free memory
            if ( e31 != NULL ) delete e31;
            if ( e21 != NULL ) delete e21;
            if ( e12 != NULL ) delete e12;
            if ( e32 != NULL ) delete e32;
            if ( e23 != NULL ) delete e23;
            if ( e13 != NULL ) delete e13;
            if ( e53 != NULL ) delete e53;
            if ( e44 != NULL ) delete e44;
            if ( e63 != NULL ) delete e63;

            //Throw exception
            throw ErrorBadAlloc ( "EErrorBadAlloc: ", "Delaunay triangulation: Can not create new triangles for inserted point p." );
    }

    //Throw exception
    catch ( ErrorMathZeroDevision &e )
    {
            //Free memory
            if ( e31 != NULL ) delete e31;
            if ( e21 != NULL ) delete e21;
            if ( e12 != NULL ) delete e12;
            if ( e32 != NULL ) delete e32;
            if ( e23 != NULL ) delete e23;
            if ( e13 != NULL ) delete e13;
            if ( e53 != NULL ) delete e53;
            if ( e44 != NULL ) delete e44;
            if ( e63 != NULL ) delete e63;

            //Throw exception
            throw ErrorBadAlloc ( "EErrorMathZeroDevision: ", "Delaunay triangulation: Can not create new triangles for inserted point p." );
    }
}

测试代码的shared_ptr:

代码被重写不使用任何优化...

void DT2D::DTInsertPoint ( std::shared_ptr <Point2D> p, std::shared_ptr <HalfEdge> *e1, HalfEdgesList * half_edges_dt )
{
    // One step of the Delaunay triangulation, incremental insertion by de Berg (2001)
    short   status = -1;

    //Pointers
    std::shared_ptr <HalfEdge> e31;
    std::shared_ptr <HalfEdge> e21;
    std::shared_ptr <HalfEdge> e12;
    std::shared_ptr <HalfEdge> e32;
    std::shared_ptr <HalfEdge> e23;
    std::shared_ptr <HalfEdge> e13;
    std::shared_ptr <HalfEdge> e53;
    std::shared_ptr <HalfEdge> e44;
    std::shared_ptr <HalfEdge> e63;

    try
    {
            // Test, if point lies inside triangle
            *e1 = LawsonOrientedWalk::findTriangleWalk ( p, &status, *e1, 0 );

            if ( e1 != NULL )
            {
                    // Edges inside triangle lies the point
                    std::shared_ptr <HalfEdge> e2((*e1 )->getNextEdge());
                    std::shared_ptr <HalfEdge> e3(e2->getNextEdge());

                    // Point lies inside the triangle
                    if ( status == 1 )
                    {
                            // Create first new triangle T1, twin edges set after creation
            e31.reset( new HalfEdge ( p, *e1, NULL ));
                            e21.reset( new HalfEdge ( e2->getPoint(), e31, NULL ));
                            ( *e1 )->setNextEdge ( e21 );

                            // Create second new triangle T2, twin edges set after creation
                            e12.reset( new HalfEdge ( p, e2, NULL ));
                            e32.reset( new HalfEdge ( e3->getPoint(), e12, NULL ));
                            e2->setNextEdge ( e32 );

                            // Create third new triangle T3, twin edges set after creation
                            e23.reset( new HalfEdge ( p, e3, NULL ));
                            e13.reset( new HalfEdge ( ( *e1 )->getPoint(), e23, NULL ));
                            e3->setNextEdge ( e13 );

                            // Set twin edges in T1, T2, T3
                            e12->setTwinEdge ( e21 );
                            e21->setTwinEdge ( e12 );
                            e13->setTwinEdge ( e31 );
                            e31->setTwinEdge ( e13 );
                            e23->setTwinEdge ( e32 );
                            e32->setTwinEdge ( e23 );

                            // Add new edges into list
                            half_edges_dt->push_back ( e21 );
                            half_edges_dt->push_back ( e12 );
                            half_edges_dt->push_back ( e31 );
                            half_edges_dt->push_back ( e13 );
                            half_edges_dt->push_back ( e32 );
                            half_edges_dt->push_back ( e23 );

                            // Legalize triangle T1
                            if ( ( *e1 )->getTwinEdge() != NULL )
                            {
                                    legalizeTriangle ( p, *e1 );
                            }

                            // Legalize triangle T2
                            if ( e2->getTwinEdge() != NULL )
                            {
                                    legalizeTriangle ( p, e2 );
                            }

                            // Legalize triangle T3
                            if ( e3->getTwinEdge() != NULL )
                            {
                                    legalizeTriangle ( p, e3 );
                            }
                    }

                    // Point lies on the edge of the triangle
                    else if ( status == 2 )
                    {
                            // Find adjacent triangle
                            std::shared_ptr <HalfEdge> e4 = ( *e1 )->getTwinEdge();
                            std::shared_ptr <HalfEdge> e5 = e4->getNextEdge();
                            std::shared_ptr <HalfEdge> e6 = e5->getNextEdge();

                            // Create first new triangle T1, twin edges set after creation
                            e21.reset(new HalfEdge ( p, e3, NULL ));
                            ( *e1 )->setNextEdge ( e21 );

                            // Create second new triangle T2, OK
                            e12.reset(new HalfEdge ( p, e2, e4 ));
                            e32.reset(new HalfEdge ( e3->getPoint(), e12, e21 ));
                            e2->setNextEdge ( e32 );

                            // Create third new triangle T3, twin edges set after creation
                            e53.reset(new HalfEdge ( p, e6, NULL ));
                            e4->setNextEdge ( e53 );

                            // Create fourth new triangle T4, OK
                            e44.reset(new HalfEdge ( p, e5, *e1 ));
                            e63.reset(new HalfEdge ( e6->getPoint(), e44, e53 ));
                            e5->setNextEdge ( e63 );

                            // Set twin edges in T1, T3
                            e21->setTwinEdge ( e32 );
                            ( *e1 )->setTwinEdge ( e44 );
                            e53->setTwinEdge ( e63 );
                            e4->setTwinEdge ( e12 );

                            // Add new edges into list
                            half_edges_dt->push_back ( e21 );
                            half_edges_dt->push_back ( e12 );
                            half_edges_dt->push_back ( e32 );
                            half_edges_dt->push_back ( e53 );
                            half_edges_dt->push_back ( e63 );
                            half_edges_dt->push_back ( e44 );

                            // Legalize triangle T1
                            if ( e3->getTwinEdge() != NULL )
                            {
                                    legalizeTriangle ( p, e3 );
                            }

                            // Legalize triangle T4
                            if ( e5->getTwinEdge() != NULL )
                            {
                                    legalizeTriangle ( p, e5 );
                            }

                            // Legalize triangle T3
                            if ( e6->getTwinEdge() != NULL )
                            {
                                    legalizeTriangle ( p, e6 );
                            }

                            // Legalize triangle T2
                            if ( e2->getTwinEdge() != NULL )
                            {
                                    legalizeTriangle ( p, e2 );
                            }
                    }
            }
    }
    //Throw exception
    catch ( std::bad_alloc &e )
    {
    /*
            //Free memory
            if ( e31 != NULL ) delete e31;
            if ( e21 != NULL ) delete e21;
            if ( e12 != NULL ) delete e12;
            if ( e32 != NULL ) delete e32;
            if ( e23 != NULL ) delete e23;
            if ( e13 != NULL ) delete e13;
            if ( e53 != NULL ) delete e53;
            if ( e44 != NULL ) delete e44;
            if ( e63 != NULL ) delete e63;
    */
            //Throw exception
            throw ErrorBadAlloc ( "EErrorBadAlloc: ", "Delaunay triangulation: Can not create new triangles for inserted point p." );
    }

    //Throw exception
    catch ( ErrorMathZeroDevision &e )
    {
    /*
            //Free memory
            if ( e31 != NULL ) delete e31;
            if ( e21 != NULL ) delete e21;
            if ( e12 != NULL ) delete e12;
            if ( e32 != NULL ) delete e32;
            if ( e23 != NULL ) delete e23;
            if ( e13 != NULL ) delete e13;
            if ( e53 != NULL ) delete e53;
            if ( e44 != NULL ) delete e44;
            if ( e63 != NULL ) delete e63;
    */
            //Throw exception
            throw ErrorBadAlloc ( "EErrorMathZeroDevision: ", "Delaunay triangulation: Can not create new triangles for inserted point p." );
    }
}

谢谢你的帮助...

编辑

我更换了所有对象的直传用别名传递及。 拷贝构造函数,然后使用之前那么频繁。

对于shared_ptr的更新的表

shared_ptr的(C ++ 0×00)的时候:

N[points]         t[sec]     
100 000                6   
200 000               11   
300 000               16    
900 000               36   

shared_ptr的(C ++ 0×00)新版本:

N[points]         t[sec]      
100 000                2  
200 000                5  
300 000                9  
900 000               24  

有一个相当大的改进,但shared_ptr的版本仍然是比原始指针的速度慢4倍。 恐怕程序的运行速度不能显著上升。

Answer 1:

shared_ptr是最复杂类型的指针永远的:

  • 参考计数需要时间
  • 多个分配(有3个部分:对象,计数器,所述删除器)
  • 许多用于类型擦除虚拟方法(在计数器和删除器)
  • 多个线程(因此同步)之间的工作原理

有2种方式,使他们更快:

  • 使用 make_shared 分配它们 ,因为(不幸)正常构造分配两个不同的块:一个用于对象和一个用于计数器和删除器。
  • 方法应该接受:如果你不需要不要复制他们 shared_ptr<T> const&

但也有很多方法不使用它们。

看你的代码,它看起来像你这样做内存分配的很多,我不禁想,如果你不能找到一个更好的策略。 我必须承认,我并没有得到充分的人物,所以我可能会直奔到墙上,但...

通常代码要简单得多,如果你有每个对象的所有者。 因此, shared_ptr应该是一个不得已的举措,当你无法得到一个单一的所有者使用。

总之,我们在这里比较苹果和桔子,原代码是马车。 你照顾deleting内存(好),但你忘了,这些物体是从其他点在节目中还引用e1->setNextEdge(e21)其现在持有的指针,以破坏对象(在free'd存储区)。 所以我想,在例外的情况下,你只是消灭整个列表? (或以某种方式投注未定义行为发挥好)

因此,它是很难在表演判断,因为前者,而后者则不会例外恢复良好。

最后:你有没有想过使用intrusive_ptr ? 它可以给你一些补偿(嘿嘿)如果不同步它们(单线程),你会避免很多由执行的东西shared_ptr以及对访问的局部性增益。



Answer 2:

我总是建议使用std :: shared_ptr的<>,而不是依靠手动内存寿命的时间管理。 然而,自动生命周期管理成本的东西,但通常不显著。

在你的情况,你注意到的shared_ptr <>是显著和一些说,你应该确保你没有不必要的副本共享指针作为力的AddRef /释放。

但是有在后台另外一个问题:你真的需要依靠新的/删除摆在首位? 新/删除应用的malloc /免费未调整的小物件分配。

这帮助了我很多以前的库是提高:: object_pool 。

在某个阶段,我想非常快的创建图形。 节点和边缘自然动态分配,我得到这样做两项费用。

  1. 的malloc /免费
  2. 存储生命周期管理

升压:object_pool有助于降低这两项费用在不是像一般的malloc /免费的成本。

因此,作为一个例子,让我们说我们有这样一个简单的节点:

   struct node
   {
      node * left;
      node * right;
   };

因此,与其用新的分配节点我使用的boost :: object_pool。 但的boost :: object_pool还跟踪与它分配的所有实例,以便在我计算我摧毁object_pool,没有需要追踪因而每个节点简化了我的代码,提高了演出结束。

我做了一些性能测试(我写我自己的游泳池类只是为了好玩,但布尔:: object_pool应该给予同样的性能或更好)。

10,000,000节点创建和销毁

  1. 纯新/删除:2.5secs
  2. shared_ptr的:5secs
  3. 提高:: object_pool:0.15secs

所以,如果升压::你object_pool作品它可以帮助显著减少内存分配开销。



Answer 3:

默认情况下,如果您创建共享指针用简单的方式(即shared_ptr<type> p( new type ) )您所发生的两个存储器分配,一个是实际物体与引用计数的额外拨款。 您可以通过利用的避免额外分配make_shared模板,将两个物体与基准计,然后就地构造对象执行单实例化。

用加倍的malloc调用,如递增和递减计数(两个原子操作)和测试相比,删除了额外费用的其余部分是相当小的。 如果你能提供您如何使用指针/共享指针一些代码,你可能会得到更好的洞察力,以什么代码实际上是怎么回事。



Answer 4:

尝试在“放”模式,看看你更接近基准。 调试模式往往在STL其中慢很多东西打开很多的断言下来。



Answer 5:

shared_ptr 明显比原始指针慢。 这就是为什么如果你确实需要共享所有权的语义,他们只应使用。

否则,有几个可用的其他智能指针类型。 scoped_ptrauto_ptr (C ++ 03)或unique_ptr (C ++ 0X)两者都具有它们的用途。 而通常情况下,最好的解决办法是不使用任何类型的指针,只写自己的RAII类代替。

一个shared_ptr有递增/递减/读取引用计数器,并根据实现,它是如何被实例化,裁判计数器可以单独分配,造成了高速缓存未命中。 而且它有访问原子裁判计数器,它增加了额外的开销。



Answer 6:

这是不可能回答这个没有更多的数据。 你有没有异型代码来准确地识别shared_ptr的版本放缓的根源? 使用容器肯定会增加开销,但我会感到惊讶,如果它使得它慢10倍。

VSTS具有良好的PERF的工具,将属性CPU使用率准确,如果你能为30秒左右,运行此。 如果您没有访问VS性能工具或其他分析工具集,然后运行在调试器中shared_ptr的代码,并打入其10或15倍以获得在那里的花费所有时间的蛮力样品。 这是令人惊讶和反直觉有效,我已经找到。

[编辑]不要值代码的变种是通过你的shared_ptr - 使用裁判常量。 如果这个函数被调用了很多,这将有可衡量的-ve影响。



Answer 7:

这是缓慢的,因为它使用的参考INC / DEC操作原子操作,因此它的horible缓慢。 如果你真的需要GC在C ++中,不使用天真RF GC,并使用一些较发达的RC策略,或跟踪GC。 http://www.hboehm.info/gc/是很好的不是速度关键任务(但不是“智能指针”天真RC好多了)。



文章来源: shared_ptr: horrible speed