shared_ptr: ужасная скорость

При сравнении двух вариантов pointerclassic и shared_ptr я был удивлен значительным увеличением скорости работы программы. Для тестирования был использован алгоритм инкрементальной вставки 2D Делоне.

Настройки компилятора:

VS 2010 (выпуск) / O2 / MD / GL, W7 Prof, CPU 3.GHZ DualCore

Результаты:

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 раза медленнее, чем версия с необработанным указателем. Боюсь, что скорость работы программы существенно не увеличится.


person Ian    schedule 02.09.2010    source источник
comment
Это также зависит от используемой реализации. Без кода для проверки результаты нельзя будет воспринимать всерьез. (например, свободны ли указатели в обеих версиях, только в одной версии или ни в одной?)   -  person luiscubal    schedule 02.09.2010
comment
Вы используете std::tr1::shared_ptr или Boost shared_ptr? VS2010 также имеет много проблем (еще не качественный IME). Вы можете где-нибудь поделиться источником?   -  person dirkgently    schedule 02.09.2010
comment
@luiscubal: Хороший аргумент в отношении высвобождения ресурсов. Если версия с необработанным указателем не освобождает память, конечно, это будет намного быстрее. Это все равно будет медленнее, потому что shared_ptr просто должен делать больше работы, но это довольно большая разница.   -  person Steve M    schedule 02.09.2010
comment
@dirkgently: я использую std :: shared_ptr. ›› Можете ли вы поделиться источником где-нибудь. Это проблема, боюсь, что не смогу. Он работает с полной топологической моделью и имеет более 40 классов :-(.   -  person Ian    schedule 02.09.2010
comment
@luiscubal: необработанные указатели освобождены, и код был проверен на наличие утечек памяти (C ++ Builder, CodeGuard). Версия shared_ptr имеет собственное управление памятью.   -  person Ian    schedule 02.09.2010
comment
Возможно, unique_ptr был бы лучшим выбором, поскольку, судя по всему, вы не делитесь ресурсом.   -  person GManNickG    schedule 02.09.2010
comment
Во-первых, переключитесь на использование эквивалента наддува, переключение должно быть легким. Тогда вы можете сообщить нам о разнице в производительности, пожалуйста.   -  person gbjbaanb    schedule 03.09.2010
comment
Совет не по теме: вам не нужно проверять, является ли указатель NULL, чтобы удалить его, просто передайте его delete независимо от его значения, и все будет в порядке.   -  person Fernando Silveira    schedule 25.08.2014


Ответы (7)


shared_ptr - это самый сложный тип указателя на свете:

  • Подсчет ссылок требует времени
  • Множественное размещение (состоит из 3 частей: объект, счетчик, удалитель)
  • Ряд виртуальных методов (в счетчике и удалителе) для стирания типа
  • Работает между несколькими потоками (таким образом, синхронизация)

Есть 2 способа сделать их быстрее:

  • используйте make_shared для их размещения, потому что (к сожалению) обычный конструктор выделяет два разных блока: один для объекта, а другой для счетчика и удаления.
  • не копируйте их, если вам это не нужно: методы должны принимать shared_ptr<T> const&

Но есть также много способов НЕ использовать их.

Глядя на ваш код, похоже, что вы делаете МНОГО распределения памяти, и я не могу не задаться вопросом, не могли ли вы найти лучшую стратегию. Должен признать, я не понял всей фигуры, так что, возможно, я направляюсь прямо в стену, но ...

Обычно код намного проще, если у вас есть владелец для каждого из объектов. Следовательно, shared_ptr должно быть крайней мерой, используемой, когда вы не можете получить единственного владельца.

Во всяком случае, мы здесь сравниваем яблоки и апельсины, в исходном коде есть ошибки. Вы заботитесь о deleting памяти (хорошо), но вы забыли, что на эти объекты также ссылались из других точек программы e1->setNextEdge(e21), которая теперь содержит указатели на разрушенные объекты (в свободной зоне памяти). Поэтому я предполагаю, что в случае исключения вы просто уничтожите весь список? (Или как-нибудь сделать ставку на неопределенное поведение, чтобы вести хорошую игру)

Так что сложно судить о производительности, поскольку первый не восстанавливается после исключений, а второй - нет.

Наконец: думали ли вы об использовании intrusive_ptr? Это может дать вам некоторый импульс (хе-хе), если вы не синхронизируете их (один поток), и вы избежите большого количества вещей, выполняемых shared_ptr, а также увеличения локальности ссылок.

person Matthieu M.    schedule 02.09.2010
comment
+1 за каламбур :). Жалко, что нет стандартного интеллектуального указателя без гарантий безопасности потока shared_ptr. Атомарная оценка количества ссылок, вероятно, стоит довольно дорого (и не учитывается в callgrind, исходя только из прошлого опыта) - person RichardBruce; 19.02.2014

Я всегда рекомендую использовать std :: shared_ptr ‹> вместо того, чтобы полагаться на ручное управление временем жизни памяти. Однако автоматическое управление сроком службы стоит чего-то, но обычно незначительного.

В вашем случае вы заметили, что shared_ptr ‹> имеет большое значение, и, как некоторые говорили, вы должны убедиться, что вы не копируете без надобности общий указатель, поскольку это заставляет addref / release.

Но есть еще один вопрос: действительно ли вам нужно полагаться на new / delete в первую очередь? new / delete использует malloc / free, которые не настроены для выделения небольших объектов.

Библиотека, которая раньше мне очень помогала, - это boost :: object_pool.

На каком-то этапе мне захотелось очень быстро создавать графики. Узлы и ребра естественным образом распределяются динамически, и я получаю от этого две затраты.

  1. malloc / бесплатно
  2. Управление временем жизни памяти

boost: object_pool помогает снизить обе эти затраты за счет того, что он не такой общий, как malloc / free.

В качестве примера предположим, что у нас есть такой простой узел:

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

Поэтому вместо узла выделения с новым я использую boost :: object_pool. Но boost :: object_pool также отслеживает весь выделенный им экземпляр, поэтому в конце моих вычислений я уничтожил object_pool, и мне не нужно было отслеживать каждый узел, что упростило мой код и повысило производительность.

Я провел некоторое тестирование производительности (я написал свой собственный класс пула просто для удовольствия, но bool :: object_pool должен давать такую ​​же производительность или лучше).

10,000,000 узлов создано и уничтожено

  1. Обычное создание / удаление: 2,5 секунды
  2. shared_ptr: 5 секунд
  3. boost :: object_pool: 0,15 секунды

Так что, если boost :: object_pool работает на вас, это может значительно снизить накладные расходы на выделение памяти.

person Just another metaprogrammer    schedule 02.09.2010

По умолчанию, если вы создаете общие указатели наивным способом (т.е. shared_ptr<type> p( new type )), вы получаете два выделения памяти: одно для фактического объекта и дополнительное выделение для счетчика ссылок. Вы можете избежать дополнительного выделения, используя шаблон make_shared, который будет выполнять единственное создание экземпляра как для объекта, так и для счетчика ссылок, а затем создавать объект на месте.

Остальные дополнительные затраты довольно малы по сравнению с удвоением вызовов malloc, такими как увеличение и уменьшение счетчика (обе атомарные операции) и тестирование на удаление. Если вы можете предоставить некоторый код, показывающий, как вы используете указатели / общие указатели, вы можете лучше понять, что на самом деле происходит в коде.

person David Rodríguez - dribeas    schedule 02.09.2010

Попробуйте его в режиме «выпуска» и посмотрите, приблизятся ли результаты тестов. В режиме отладки обычно включается множество утверждений в STL, что замедляет работу.

person Ken Simon    schedule 02.09.2010
comment
Кроме того, отладочная версия STL в MSVC имеет глобальную блокировку, которая не позволяет получить преимущества использования нескольких ядер. См. stackoverflow.com/questions/2832023/ - person Didier Trosset; 02.09.2010
comment
/ MD означает, что это уже режим выпуска. / MDd будет отладкой. Я согласен с тем, что профилирование в Debug было бы пустой тратой времени, но, похоже, здесь это не так. msdn.microsoft.com/en-us/library/2kzt1wy3 (VS.80) .aspx - person Steve Townsend; 02.09.2010
comment
В отладочной версии MSVC также есть ужасная вещь под названием _HAS_ITERATOR_DEBUGGING, которая сильно тормозит. - person dirkgently; 02.09.2010
comment
Ах, я совсем забыл посмотреть на ваши cflags. Я бы согласился с тем, что говорили другие: убедитесь, что ваши указатели действительно удаляются в вашем исходном коде указателя, иначе вы сравниваете яблоки с апельсинами и в основном говорите, что мой код с утечкой памяти быстрее, чем мой код без утечки. - person Ken Simon; 02.09.2010
comment
@dirkgently - отладка итератора помогла мне отловить множество ошибок. Мне это нравится. Вы всегда можете выключить его, если производительность слишком низкая! - person AshleysBrain; 02.09.2010

shared_ptr заметно медленнее, чем необработанные указатели. Вот почему их следует использовать только в том случае, если вам действительно нужна семантика совместного владения.

В противном случае доступны несколько других типов интеллектуальных указателей. scoped_ptr и auto_ptr (C ++ 03) или unique_ptr (C ++ 0x) имеют свое применение. И часто лучшим решением является не использовать какой-либо указатель, а вместо этого просто написать свой собственный класс RAII.

shared_ptr должен увеличивать / уменьшать / считывать счетчик ссылок, и в зависимости от реализации и того, как он создается, счетчик ссылок может выделяться отдельно, что вызывает потенциальные промахи в кэше. И он должен обращаться к счетчику ссылок атомарно, что добавляет дополнительные накладные расходы.

person jalf    schedule 02.09.2010

Без дополнительных данных ответить на этот вопрос невозможно. Вы профилировали код, чтобы точно определить источник замедления в версии shared_ptr? Использование контейнера, безусловно, добавит накладных расходов, но я был бы удивлен, если бы он стал в 10 раз медленнее.

VSTS имеет хорошие инструменты perf, которые точно определяют использование ЦП, если вы можете запустить это в течение 30 секунд или около того. Если у вас нет доступа к VS Performance Tools или другому набору инструментов профилирования, запустите код shared_ptr в отладчике и взломайте его 10 или 15 раз, чтобы получить грубый пример того, на что он тратит все свое время. Я обнаружил, что это на удивление и парадоксально эффективно.

[EDIT] Не передавайте свой shared_ptr по значению в этом варианте кода - используйте ref для const. Если эта функция вызывается часто, это будет иметь ощутимое влияние.

person Steve Townsend    schedule 02.09.2010
comment
Профилировщик VS2010: для необработанных указателей большая часть времени тратится на эвристическое построение треугольника. Но для версии shared_ptr 58% времени тратится на shared_ptr.reset () - person Ian; 02.09.2010
comment
Насколько легко вам или иначе можно повторить этот анализ с помощью boost :: shared_ptr? - person Steve Townsend; 02.09.2010
comment
Спасибо за замечание ... Как я мог забыть использовать & ... Я переписал код и результаты стали лучше. Но код все еще работает намного медленнее, посмотрите правки, пожалуйста ... - person Ian; 02.09.2010
comment
Отлично - теперь откуда вызывается shared_ptr.reset ()? если это (было?) 58% вашего времени, то сокращение количества звонков должно быть большим выигрышем. У вас есть обновленный анализ для тюнинга? v трудно помочь без полного исходного кода. - person Steve Townsend; 02.09.2010

Он медленный, потому что он использует для справочных операций inc / dec атомарные инструкции, поэтому он ужасно медленный. Если вам действительно нужен GC на C ++, не используйте наивный RF GC, а используйте более развитую стратегию RC или трассировку GC. http://www.hboehm.info/gc/ хорош для задач, не требующих ускорения (но Намного лучше, чем "умные указатели" наивного RC).

person dev1223    schedule 04.01.2016