Странная (огромная) разница в производительности между std :: vector, QList и std :: list

При написании программы, которая обрабатывает относительно большое количество элементов (~ 100k), я заметил странную разницу между std :: list и QList. Сначала я использовал std :: vector, который хорошо работает. Но поскольку программе часто требуется вставлять элементы в случайную позицию в векторе, я переключился на std :: list, у которого было постоянное время для вставки, когда итератор находится в нужной позиции.

Проблема в том, что std :: list работает хуже, чем std :: vector как с методом insert (), так и с методом push_back (). Измерено для добавления 100 последовательных элементов в список из 100 тыс. Элементов, которые я получил:

  • ~ 25 мс для std :: vector (худший случай при добавлении в начале)
  • более 600 мс (!) для std :: list (любая позиция, даже с push_back () или insert (list.begin ()).

Обратите внимание, что время вставки элементов не включает время достижения позиции с итератором.

Я знаю о проблемах с производительностью списков из-за того, что список не попадает в кеш-память, но, похоже, это выходит за рамки ограничений для кеш-промаха. Также время для вставки элемента (простой структуры с 5 переменными постоянной длины) увеличивается с размером списка. Даже операция по получению размера списка занимает больше времени. Это полностью контрастирует с временной сложностью, гарантированной для обеих операций для списка: constant.

См. здесь

Просто из любопытства изменено с std :: list на QList и альт: время вставки постоянно и составляет от 0 мс до 1 мс.

Вот код, используемый для измерения времени вставки.

Никакие другие операции не выполняются между двумя точками времени: НЕПРАВИЛЬНО: использовался метод size ()

std :: List:

    QTime time;
    time.start();

    for (int a = 1; a <= lineChange; a++)
    {
        listData.insert(listIterator, newElement); 
    }        
    int elapsed = time.restart();
    qDebug() << "elapsed: " << elapsed << "ms";

Результат: прошло: 662 мс

QList:

    QTime time;
    time.start();

    for (int a = 1; a <= lineChange; a++)
    {
        QListData.insert(iteratorPos, newElement);
        position++;
    } 

    int elapsed = time.restart();
    qDebug() << "elapsed: " << elapsed << "ms";

Результат: прошло: 1 мс

std :: vector:

    QTime time;
    time.start();

    for (int a = 1; a <= lineChange; a++)
    {
        vectorData.insert(vectorIterator, newElement);

        /*update of the iterator when it was 
        invalidated due to resize of the vector*/
    }    

    int elapsed = time.restart();
    qDebug() << "elapsed: " << elapsed << "ms";

Результат: прошло: 27 мс

Итак, почему существует такая огромная разница между QList и std :: list? Или лучше: почему производительность, если std :: list такая катастрофическая?

В качестве дополнительной информации: я использую QtEditor с gcc под Linux (Mint) с флагами, установленными на c ++ 11

РЕДАКТИРОВАТЬ:

Типы данных и объявления:

typedef struct TOKELEMENT {
    unsigned int column;
    unsigned int lenght;
    unsigned int tType;
    std::string value;
} tokElement;

// the three lists
std::vector<okElement> vectorData;
std::list<tokElement> listData;
QList<tokElement> QListData;

tokElement newElement;

unsigned int iteratorPos;
std::vector<std::vector<tokElement> >::iterator vectorIterator;
std::list<std::vector<tokElement> >::iterator listIterator;

//lineChange is an unsigned int, given as function parameter
unsigned int lineChange;

person nils277    schedule 16.03.2014    source источник
comment
Предоставьте SSCCE.   -  person dyp    schedule 16.03.2014
comment
Без контекста (SSCCE) мы можем только догадываться, что происходит. Я мог бы предположить, например, что QList может хранить только указатель (в зависимости от типа элемента), тогда как list и vector хранят по значению. Кроме того, я мог предположить, что list не хранит свободный список и поэтому должен выделять его для каждого вставленного элемента.   -  person dyp    schedule 16.03.2014
comment
Можете ли вы показать достаточно кода, чтобы увидеть объявление всех соответствующих переменных, особенно ElementList, actIterator и lineChange?   -  person user2357112 supports Monica    schedule 16.03.2014
comment
Также обратите внимание, что QList не является связанным списком. Из docs: внутри QList ‹T› представлен как массив указателей на элементы типа T. Если T сам является типом указателя или базовым типом, который не больше указателя, или если T является одним из общих классов Qt, то QList ‹T› сохраняет элементы непосредственно в указателе множество.   -  person user2357112 supports Monica    schedule 16.03.2014
comment
Вы упомянули, что size () работает плохо - вероятно, это связано с тем, что gcc в этом случае не поддерживает новый стандарт. См. - stackoverflow.com/questions / 19154205 /   -  person Leeor    schedule 16.03.2014
comment
Нам нужны как минимум объявления и типы. Кроме того, компилируете ли вы как минимум -O2 и -DNDEBUG?   -  person Potatoswatter    schedule 16.03.2014
comment
Извините за задержку, я попытался создать SSCCE для отображения, но ошибки там не было. Пост буду обновлять объявлениями. Но я думаю, что нашел проблему. Я хочу проверить размер списка в цикле for, чтобы определить, использовать ли push_back () или insert (). Как сказал @Leeor, это временная сложность не O (1). Извините за неудобства   -  person nils277    schedule 16.03.2014
comment
Я не могу ответить на ваш вопрос, но из любопытства: вы вставляете в уже отсортированный список?   -  person a.lasram    schedule 16.03.2014
comment
@ a.lasram Когда вы имеете в виду, что если я когда-либо вызывал list :: sort () в списке, то нет. С другой стороны, список сортируется в том смысле, что элементы вставляются или удаляются с позиций, которые они должны занять. Сортировка с помощью list :: sort () не требуется.   -  person nils277    schedule 16.03.2014


Ответы (1)


В отличие от того, что я упомянул в вопросе (позор мне), у меня была еще одна проверка размера std :: list в for-loop, чтобы определить, использовать insert () или push_back () в списке. Поскольку эта функция не имеет временной сложности O (1), но O (n), это сильно замедлило всю вставку. Спасибо Leeor за указание на это.

После перемещения этой проверки из for-loop std :: list выполнялся так, как ожидалось, и даже быстрее, чем QList.

person nils277    schedule 16.03.2014
comment
Обратите внимание, что хорошо написанная версия std::vector будет использовать алгоритм массовой вставки для добавления элементов (возможно, по мере прохождения) вместо цикла. Чтобы список был справедливым, вы должны находить место вставки на несколько порядков реже, чем вы вставляете в этой позиции, и вам нужно использовать контейнер между вставки (при желании почти всегда сохранять позицию вставки). - person Yakk - Adam Nevraumont; 29.06.2016