push_back списка STL получил плохую производительность? - PullRequest
1 голос
/ 07 апреля 2010

Я написал простую программу для проверки производительности списков STL в сравнении с простой структурой данных, подобной списку C.Это показывает плохую производительность в строке "push_back ()".Есть комментарии?

$ ./test2
 Build the type list : time consumed -> 0.311465
 Iterate over all items: time consumed -> 0.00898
 Build the simple C List: time consumed -> 0.020275
 Iterate over all items: time consumed -> 0.008755

Исходный код:

#include <stdexcept>
#include "high_resolution_timer.hpp"

#include <list>
#include <algorithm>
#include <iostream>


#define TESTNUM 1000000

/* The test struct */
struct MyType {
    int num;
};


/*
 * C++ STL::list Test
 */
typedef struct MyType* mytype_t;

void myfunction(MyType t) {
}

int test_stl_list()
{
    std::list<mytype_t> mylist;
    util::high_resolution_timer t;

    /*
     * Build the type list
     */
    t.restart();
    for(int i = 0; i < TESTNUM; i++) {
        mytype_t aItem;
        aItem->num = i;
        mylist.push_back(aItem);
    }
    std::cout << " Build the type list : time consumed -> " << t.elapsed() << std::endl;


    /*
     * Iterate over all item
     */
    t.restart();
    std::for_each(mylist.begin(), mylist.end(), myfunction);
    std::cout << " Iterate over all items: time consumed -> " << t.elapsed() << std::endl;

    return 0;
}

/*
 * a simple C list
 */
struct MyCList;
struct MyCList{
    struct MyType m;
    struct MyCList* p_next;
};

int test_simple_c_list()
{
    struct MyCList* p_list_head = NULL;
    util::high_resolution_timer t;

    /*
     * Build it
     */
    t.restart();
    struct MyCList* p_new_item = NULL;
    for(int i = 0; i < TESTNUM; i++) {
        p_new_item = (struct MyCList*) malloc(sizeof(struct MyCList));
        if(p_new_item == NULL) {
            printf("ERROR : while malloc\n");
            return -1;
        }
        p_new_item->m.num = i;
        p_new_item->p_next = p_list_head;
        p_list_head = p_new_item;
    }
    std::cout << " Build the simple C List: time consumed -> " << t.elapsed() << std::endl;

    /*
     * Iterate all items
     */
    t.restart();
    p_new_item = p_list_head;
    while(p_new_item->p_next != NULL) {
        p_new_item = p_new_item->p_next;
    }
    std::cout << " Iterate over all items: time consumed -> " << t.elapsed() << std::endl;


    return 0;
}

int main(int argc, char** argv)
{
    if(test_stl_list() != 0) {
        printf("ERROR: error at testcase1\n");
        return -1;
    }

    if(test_simple_c_list() != 0) {
        printf("ERROR: error at testcase2\n");
        return -1;
    }

    return 0;
}

Упс, да.Я изменил код, и он показывает:

$ ./test2
 Build the type list : time consumed -> 0.163724
 Iterate over all items: time consumed -> 0.005427
 Build the simple C List: time consumed -> 0.018797
 Iterate over all items: time consumed -> 0.004778

Итак, мой вопрос, почему мой код "push_back" получил плохую производительность?

Ответы [ 4 ]

3 голосов
/ 07 апреля 2010

Ну, во-первых, в C у вас есть связанный список объектов, но в C ++ у вас есть связанный список указателей (поэтому, во-первых, вы делаете в два раза больше выделений).Чтобы сравнить яблоки с яблоками, ваш код STL должен быть:

int test_stl_list()
{
    std::list<MyType> mylist;
    util::high_resolution_timer t;

    /*
     * Build the type list
     */
    t.restart();
    for(int i = 0; i < TESTNUM; i++) {
        MyItem aItem;
        aItem.num = i;
        mylist.push_back(aItem);
    }
    std::cout << " Build the type list : time consumed -> " << t.elapsed() << std::endl;


    return 0;
}
0 голосов
/ 07 апреля 2010

Ваши коды STL создают фрагмент памяти дважды для каждой ячейки. Следующее от STL 4.1.1 на x86_64

void push_back(const value_type& __x)
{
   this->_M_insert(end(), __x);
}


// Inserts new element at position given and with value given.
void _M_insert(iterator __position, const value_type& __x)
{
   _Node* __tmp = _M_create_node(__x);     // Allocate a new space ####
   __tmp->hook(__position._M_node);
}

Как вы можете видеть, функция push_back () вызывает еще несколько функций, прежде чем вернуться к вызывающей стороне, и копирование нескольких значений указателей происходит каждый раз, когда вызывается одна из функций. Может быть неприемлемым, потому что все параметры передаются по const-ссылке.

0 голосов
/ 07 апреля 2010

Казалось бы, ваш high_resolution_timer класс измеряет больше, чем просто процедуры, которые вы пытаетесь измерить. Я бы реорганизовал код так, чтобы код only между t.restart() и t.elapsed() был тем, что вы хотели бы измерить. Весь другой код в нем может иметь неизвестные последствия для производительности, которые могут исказить ваши результаты.

0 голосов
/ 07 апреля 2010

Во-первых, похоже, что вы делаете push_front, а не push_back (в вашей собственной реализации).

Во-вторых, вы также должны сравнить std::slist для достоверного сравнения, поскольку std::list является двойной связью.

В-третьих, вам нужно использовать правильные флаги компилятора для честного сравнения. С gcc вы должны хотя бы скомпилировать с -O2. Без оптимизации STL всегда отстой, потому что никакого встраивания не выполняется и много служебных вызовов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...