shared_ptr:速度慢得可怕

52

在比较指针的两种变体——传统指针和shared_ptr时,我惊讶地发现程序的运行速度显著增加了。测试使用了2D Delaunay递增插入算法。

编译器设置:

VS 2010(发布版)/O2 /MD /GL,W7 Prof,CPU 3.GHZ双核

结果:

shared_ptr(C++ 0x00):

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++ 0x00) 旧版:

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

shared_ptr(C++ 0x00)的新版本:

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

虽然有显著的改进,但shared_ptr版本仍比原始指针慢4倍。恐怕程序的运行速度无法显著提高。


3
这也取决于所使用的实现方式。如果没有代码进行测试,就不能认真对待结果。(例如,在两个版本中,是否释放了指针,或者只在一个版本中释放,还是都没有释放?) - luiscubal
1
你使用的是 std::tr1::shared_ptr 还是 Boost 的 shared_ptr?VS2010存在很多问题(在我的经验中尚未达到生产质量)。你能否分享一下源代码? - dirkgently
1
@luiscubal:关于释放内存的问题,你说得很好。如果使用原始指针版本没有释放内存,那当然会更快。尽管shared_ptr需要做更多的工作,但它仍然会比原始指针慢,这是一个相当大的差异。 - Steve M
3
也许 unique_ptr 是一个更好的选择,因为从代码来看,你并没有在共享这个资源。 - GManNickG
一个与主题无关的提示:您不必检查指针是否为“NULL”就可以删除它,只需将其传递给“delete”,无论其值如何,都可以正常运行。 - Fernando Silveira
显示剩余3条评论
7个回答

80

shared_ptr是最复杂的指针类型:

  • 引用计数需要时间
  • 多重分配(有三个部分:对象、计数器和删除器)
  • 一些虚拟方法(在计数器和删除器中)进行类型擦除
  • 可以在多个线程之间工作(因此需要同步)

有两种方法可以使它们更快:

  • 使用 make_shared 来分配内存,因为(不幸的是)普通构造函数分配了两个不同的块:一个用于对象,一个用于计数器和删除器。
  • 如果不需要复制,请勿复制:方法应接受 shared_ptr<T> const&

但也有很多不适合使用它们的方式。

看着你的代码,似乎你正在进行大量的内存分配,我不禁想知道你是否能找到更好的策略。我必须承认我没有完全弄清楚,所以可能会直接撞向墙壁,但是...

通常情况下,如果每个对象都有一个所有者,代码会简单得多。因此,在无法获得单个所有者时,应该将 shared_ptr 作为最后一道手段。

无论如何,我们在比较苹果和橙子,原始代码有缺陷。你负责删除内存(很好),但你忘记了这些对象也被程序中其他地方引用,例如e1->setNextEdge(e21),它现在持有对已销毁对象的指针(在已释放的内存区域中)。因此,我猜,在发生异常时,你只能清除整个列表?(或以某种方式赌Undefined Behavior会变得友好?)

因此,很难判断性能,因为前者无法从异常中恢复,而后者可以。

最后,您想过使用intrusive_ptr吗?如果您不需要进行同步(单线程),它可以帮助您提高性能,而且避免了很多由shared_ptr执行的操作,并提高了引用的局部性。


很遗憾,没有标准的智能指针可以不提供shared_ptr的线程安全保证。原子地评估引用计数可能会花费相当多的时间(而且从过去的经验来看,在callgrind中也没有很好地记录)。 - RichardBruce
我建议根本不要使用const引用来处理共享指针。如果你要拥有它,就通过值传递shared_ptr并使用std::move。否则,只需使用原始指针或引用即可。(对于shared_ptr的const引用无论如何都不能处理对象的生命周期) - Lasersköld

25

我总是推荐使用std::shared_ptr<>代替手动内存生命周期管理。但是,自动生命周期管理的成本虽然存在,但通常不显著。

在你的情况中,你注意到shared_ptr<>很重要,正如一些人所说,你应该确保不会不必要地复制共享指针,因为那会强制进行addref/release操作。

但背后还有另一个问题:你真的需要依赖new/delete吗?new/delete使用malloc/free,这些函数并不适用于小对象的分配。

一个之前帮助过我的库是boost::object_pool

在某个阶段,我想要快速创建图形。节点和边自然而然地是动态分配的,这导致了两个开销。

  1. malloc/free
  2. 内存生命周期管理

boost:object_pool可以帮助减少这两种开销,但代价是没有malloc/free那么通用。

所以,举个例子,假设我们有一个像这样简单的节点:

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

因此,我使用boost::object_pool来分配节点,而不是使用new进行内存分配。但是,boost::object_pool也会跟踪所有用它分配的实例,因此在计算结束时,我销毁了object_pool并且不需要跟踪每个节点,从而简化了我的代码并提高了性能。

我进行了一些性能测试(我自己编写了一个池类,仅出于兴趣,但bool::object_pool应该提供相同或更好的性能)。

创建并销毁了10,000,000个节点

  1. 普通new/delete:2.5秒
  2. shared_ptr:5秒
  3. boost::object_pool:0.15秒

因此,如果boost::object_pool适合您的情况,它可能有助于显着降低内存分配开销。


12

默认情况下,如果您以朴素的方式创建共享指针(即shared_ptr<type> p( new type )),则会产生两个内存分配,一个用于实际对象,另一个用于引用计数。您可以通过使用make_shared 模板来避免额外的内存分配,该模板将为对象和引用计数执行单个实例化,然后在原地构造对象。

与调用malloc两次相比,其余额外成本非常小,例如增加和减少计数(都是原子操作)以及测试删除。如果您可以提供一些代码来说明您如何使用指针/共享指针,则可能会更好地了解实际上正在代码中发生的事情。


8

尝试在“发布”模式下运行并查看是否可以获得更接近的基准测试结果。调试模式往往会在STL中打开许多断言,这会使许多操作变慢。


此外,MSVC中STL的Debug版本具有全局锁定,防止利用多个核心的优势。请参见https://dev59.com/lE3Sa4cB1Zd3GeqPyMgt#2832511。 - Didier Trosset
2
/MD意味着这已经是发布模式。/MDd则是调试模式。我同意在调试模式下进行性能分析是浪费时间的,但这里似乎并非如此。 http://msdn.microsoft.com/zh-cn/library/2kzt1wy3(VS.80).aspx - Steve Townsend
MSVC的调试版本还有一个可怕的东西叫做_HAS_ITERATOR_DEBUGGING,它会使事情变得非常缓慢。 - dirkgently
啊,我完全忘记看你的cflags了。我会按照其他人说的去做:确保你的指针在原始指针代码中实际上被删除,否则你就是在比较苹果和橙子,基本上是在说“我的内存泄漏代码比我的非泄漏代码更快”。 - Ken Simon
4
@dirkgently - 迭代器调试帮助我捕获了许多错误,我很喜欢它。如果性能太差,您总是可以关闭它! - AshleysBrain

8
shared_ptr与原始指针相比明显较慢。因此,只有在实际需要共享所有权语义时才应使用它们。

否则,还有几种其他的智能指针类型可用。例如,scoped_ptrauto_ptr(C++03)或unique_ptr(C++0x)都有其用途。通常,最好的解决方案不是使用任何类型的指针,而是编写自己的RAII类。

shared_ptr必须增加/减少/读取引用计数器,并且根据实现方式和实例化方式,引用计数器可能会被单独分配,从而导致潜在的缓存失效。它还必须原子地访问引用计数器,这会增加额外的开销。


2
没有更多的数据,无法回答这个问题。您是否已经对代码进行了剖析,以准确地确定 shared_ptr 版本的性能瓶颈?使用容器肯定会增加开销,但如果它使其变慢 10 倍,我会感到惊讶。
VSTS 有很好的性能工具,如果您可以运行大约 30 秒钟,它将精确地归因于 CPU 使用情况。如果您没有访问 VS 性能工具或其他剖析工具集,则在调试器中运行 shared_ptr 代码,并中断 10 或 15 次,以获得它花费所有时间的暴力样本。我发现这是出乎意料和反直觉的有效方法。
[编辑] 不要在该代码变量中传递 shared_ptr 的值 - 使用 ref to const。如果此函数被频繁调用,这将产生可测量的负面影响。

VS2010 Profiler:对于原始指针,大部分时间都花在了发现三角形的启发式算法上。但是对于shared_ptr版本,58%的时间是在使用shared_ptr.reset()上花费的。 - Ian
你觉得使用boost::shared_ptr重新尝试这个分析有多容易或者难度如何? - Steve Townsend
谢谢您的评论...我怎么会忘记使用&...我重写了代码,结果更好了。但是代码仍然慢得多,请看编辑... - Ian
太好了 - 现在,shared_ptr.reset()是从哪里调用的?如果这占据了你58%的时间,那么减少调用次数应该会有很大的收益。你是否有更新的调优分析?不过没有完整的源代码很难提供帮助。 - Steve Townsend

2

它很慢,因为它使用对 inc/dec 操作原子指令的引用,因此非常慢。如果您真的需要在 C++ 中使用 GC,请不要使用天真的 RF GC,并使用一些更发达的 RC 策略或追踪 GC。对于不太注重速度的任务,http://www.hboehm.info/gc/ 是不错的选择(但比“智能指针”天真的 RC 更好)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接