Я всегда думал, что это общая мудрость std::vector
"реализована в виде массива", бла-бла-бла. Сегодня я спустился и проверил это, и, кажется, это не так:
Вот некоторые результаты теста:
UseArray completed in 2.619 seconds
UseVector completed in 9.284 seconds
UseVectorPushBack completed in 14.669 seconds
The whole thing completed in 26.591 seconds
Это примерно в 3 - 4 раза медленнее! На самом деле не оправдывает vector
комментарии « может быть медленнее для нескольких наносек».
И код, который я использовал:
#include <cstdlib>
#include <vector>
#include <iostream>
#include <string>
#include <boost/date_time/posix_time/ptime.hpp>
#include <boost/date_time/microsec_time_clock.hpp>
class TestTimer
{
public:
TestTimer(const std::string & name) : name(name),
start(boost::date_time::microsec_clock<boost::posix_time::ptime>::local_time())
{
}
~TestTimer()
{
using namespace std;
using namespace boost;
posix_time::ptime now(date_time::microsec_clock<posix_time::ptime>::local_time());
posix_time::time_duration d = now - start;
cout << name << " completed in " << d.total_milliseconds() / 1000.0 <<
" seconds" << endl;
}
private:
std::string name;
boost::posix_time::ptime start;
};
struct Pixel
{
Pixel()
{
}
Pixel(unsigned char r, unsigned char g, unsigned char b) : r(r), g(g), b(b)
{
}
unsigned char r, g, b;
};
void UseVector()
{
TestTimer t("UseVector");
for(int i = 0; i < 1000; ++i)
{
int dimension = 999;
std::vector<Pixel> pixels;
pixels.resize(dimension * dimension);
for(int i = 0; i < dimension * dimension; ++i)
{
pixels[i].r = 255;
pixels[i].g = 0;
pixels[i].b = 0;
}
}
}
void UseVectorPushBack()
{
TestTimer t("UseVectorPushBack");
for(int i = 0; i < 1000; ++i)
{
int dimension = 999;
std::vector<Pixel> pixels;
pixels.reserve(dimension * dimension);
for(int i = 0; i < dimension * dimension; ++i)
pixels.push_back(Pixel(255, 0, 0));
}
}
void UseArray()
{
TestTimer t("UseArray");
for(int i = 0; i < 1000; ++i)
{
int dimension = 999;
Pixel * pixels = (Pixel *)malloc(sizeof(Pixel) * dimension * dimension);
for(int i = 0 ; i < dimension * dimension; ++i)
{
pixels[i].r = 255;
pixels[i].g = 0;
pixels[i].b = 0;
}
free(pixels);
}
}
int main()
{
TestTimer t1("The whole thing");
UseArray();
UseVector();
UseVectorPushBack();
return 0;
}
Я делаю это неправильно или что-то? Или я только что сломал этот миф о спектакле?
Я использую режим выпуска в Visual Studio 2005 .
В Visual C ++ , #define _SECURE_SCL 0
уменьшает UseVector
вдвое (доведя его до 4 -х секунд). Это действительно огромно, ИМО.
vector
это массив общего назначения с изменяемыми размерами. Поздравляю. Как и со всеми инструментами общего назначения, можно придумать специализированные ситуации, когда они неоптимальны. Вот почему общепринятая мудрость заключается в том, чтобы начать сvector
и рассмотреть альтернативы, если это необходимо.Ответы:
Используя следующее:
Таким образом, массив в два раза быстрее, чем вектор.
Но после более подробного изучения кода это ожидается; когда вы пересекаете вектор дважды, а массив - только один раз. Примечание: когда вы
resize()
используете вектор, вы не только выделяете память, но и пробегаете вектор и вызываете конструктор для каждого члена.Немного перестроим код, чтобы вектор инициализировал каждый объект только один раз:
Теперь снова делаем то же время:
Вектор теперь производительность лишь немного хуже, чем массив. ИМО, эта разница незначительна и может быть вызвана целой кучей вещей, не связанных с тестом.
Я также хотел бы принять во внимание, что вы неправильно инициализируете / уничтожаете объект Pixel в
UseArrray()
методе, так как не вызывается ни конструктор / деструктор (это может быть не проблема для этого простого класса, но что-то более сложное (например, с указателями или членами) с указателями) вызовет проблемы.источник
reserve()
вместоresize()
. Это выделяет пространство для объектов (то есть изменяет емкость вектора), но не создает объекты (то есть размер вектора остается неизменным).resize()
наreserve()
, потому что это не корректирует внутреннее представление вектора о его собственном размере, поэтому последующие записи в его элементы технически «записывают за конец» и приводят к UB. Хотя на практике каждая реализация STL будет «вести себя» в этом отношении, как вы синхронизируете размер вектора? Если вы попытаетесь вызватьresize()
после заполнения вектора, вполне вероятно, что все эти элементы будут перезаписаны значениями, созданными по умолчанию!reserve
меняется только емкость вектора, а не его размер./EHsc
к ключам компиляции убрало это, иassign()
фактически превосходит массив. Ура.Отличный вопрос Я пришел сюда, ожидая найти какое-то простое исправление, которое ускорит векторные тесты. Это не сработало так, как я ожидал!
Оптимизация помогает, но этого недостаточно. С оптимизацией я все еще вижу разницу в производительности в 2 раза между UseArray и UseVector. Интересно, что UseVector был значительно медленнее, чем UseVectorPushBack без оптимизации.
Идея № 1 - использовать новый [] вместо malloc
Я попытался изменить ,
malloc()
чтобыnew[]
в UseArray поэтому объекты будут получать построены. И переход от индивидуального назначения поля к назначению экземпляра Pixel. Да, и переименование переменной внутреннего цикла вj
.Удивительно (для меня), ни одно из этих изменений не имело никакого значения вообще. Даже изменение, к
new[]
которому по умолчанию будут построены все пиксели. Кажется, что gcc может оптимизировать вызовы конструктора по умолчанию при использованииnew[]
, но не при использованииvector
.Идея № 2 - Удаление повторных вызовов оператора []
Я также попытался избавиться от тройного
operator[]
поиска и кешировать ссылку наpixels[j]
. Это на самом деле замедлило UseVector! К сожалению.Идея № 3 - Удалить конструкторов
Как насчет полного удаления конструкторов? Тогда, возможно, gcc сможет оптимизировать построение всех объектов при создании векторов. Что произойдет, если мы изменим Pixel на:
Результат: примерно на 10% быстрее. Все еще медленнее, чем массив. Гектометр
Идея № 4 - использовать итератор вместо индекса цикла
Как насчет использования
vector<Pixel>::iterator
вместо индекса цикла?Результат:
Нет, не отличается. По крайней мере, это не медленнее. Я думал, что это будет иметь производительность, аналогичную # 2, где я использовал
Pixel&
ссылку.Вывод
Даже если некоторые умные cookie-файлы выясняют, как сделать векторный цикл таким же быстрым, как и массив, это не очень хорошо говорит о поведении по умолчанию
std::vector
. Так много для того, чтобы компилятор был достаточно умен, чтобы оптимизировать всю C ++ и сделать контейнеры STL такими же быстрыми, как необработанные массивы.Суть в том, что при использовании компилятор не может оптимизировать вызовы конструктора no-op по умолчанию
std::vector
. Если вы используете обычный,new[]
он отлично их оптимизирует. Но не сstd::vector
. Даже если вы можете переписать свой код, чтобы исключить вызовы конструктора, которые выглядят здесь как мантра: «Компилятор умнее вас. STL такой же быстрый, как простой C. Не беспокойтесь об этом».источник
vector<int>
.Это старый, но популярный вопрос.
На данный момент многие программисты будут работать на C ++ 11. А в C ++ 11 написанный код OP работает одинаково быстро для
UseArray
илиUseVector
.Основная проблема заключалась в том, что в то время как ваша
Pixel
структура была неинициализирована, онаstd::vector<T>::resize( size_t, T const&=T() )
принимает построенный по умолчаниюPixel
и копирует его . Компилятор не заметил, что его просили скопировать неинициализированные данные, поэтому он фактически выполнил копирование.В C ++ 11
std::vector<T>::resize
есть две перегрузки. Первоеstd::vector<T>::resize(size_t)
, другоеstd::vector<T>::resize(size_t, T const&)
. Это означает, что когда вы вызываетеresize
без второго аргумента, он просто конструирует по умолчанию, и компилятор достаточно умен, чтобы понять, что конструкция по умолчанию ничего не делает, поэтому пропускает пропуск через буфер.(Две перегрузки были добавлены для обработки подвижных, конструируемых и не копируемых типов - повышение производительности при работе с неинициализированными данными является преимуществом).
push_back
Решение также делает fencepost проверку, которая замедляет его вниз, так что остается медленнее , чемmalloc
версия.живой пример (я также заменил таймер
chrono::high_resolution_clock
).Обратите внимание, что если у вас есть структура, которая обычно требует инициализации, но вы хотите обработать ее после увеличения буфера, вы можете сделать это с помощью специального
std::vector
распределителя. Если вы хотите затем перевести его в более нормальное руслоstd::vector
, я считаю, что осторожное использованиеallocator_traits
и переопределение==
могут это осуществить, но я не уверен.источник
emplace_back
противpush_back
.clang++ -std=c++11 -O3
имеетUseArray completed in 2.02e-07 seconds
иUseVector completed in 1.3026 seconds
. Я также добавилUseVectorEmplaceBack
версию, которая составляет ок. В 2,5 раза быстрееUseVectorPushBack
.Если честно, вы не можете сравнить реализацию C ++ с реализацией C, как я бы назвал вашу версию malloc. malloc не создает объекты - он только выделяет необработанную память. То, что вы затем обрабатываете эту память как объекты без вызова конструктора, является плохим C ++ (возможно, недействительным - я оставлю это юристам по языку).
Тем не менее, простое изменение malloc на
new Pixel[dimensions*dimensions]
и free наdelete [] pixels
не имеет большого значения с простой реализацией Pixel, которая у вас есть. Вот результаты на моей коробке (E6600, 64-битная версия):Но с небольшим изменением таблицы поворачиваются:
Pixel.h
Pixel.cc
main.cc
Скомпилировано так:
мы получаем очень разные результаты:
С помощью встроенного конструктора для Pixel std :: vector теперь превосходит необработанный массив.
Может показаться, что сложность выделения через std :: vector и std: allocator слишком велика, чтобы ее можно было оптимизировать так же эффективно, как и простую
new Pixel[n]
. Тем не менее, мы видим, что проблема заключается просто в распределении, а не в доступе к вектору, путем настройки нескольких тестовых функций для создания вектора / массива один раз путем перемещения его за пределы цикла:и
Мы получаем эти результаты сейчас:
Из этого мы можем узнать, что std :: vector сравним с необработанным массивом для доступа, но если вам нужно многократно создавать и удалять вектор / массив, создание сложного объекта займет больше времени, чем создание простого массива. когда конструктор элемента не встроен. Я не думаю, что это очень удивительно.
источник
Попробуйте с этим:
Я получаю почти такую же производительность, как с массивом.
Дело в том,
vector
что это гораздо более общий инструмент, чем массив. А это значит, что вы должны подумать как вы его используете. Его можно использовать по-разному, предоставляя функции, которых у массива даже нет. И если вы используете это «неправильно» для своих целей, вы несете много накладных расходов, но если вы используете это правильно, это обычно в основном структура данных с нулевыми издержками. В этом случае проблема заключается в том, что вы отдельно инициализируете вектор (в результате чего для всех элементов вызывается их ctor по умолчанию), а затем перезаписываете каждый элемент по отдельности с правильным значением. Компилятору гораздо сложнее оптимизировать, чем когда вы делаете то же самое с массивом. Вот почему вектор предоставляет конструктор, который позволяет вам делать именно это: .N
X
И когда вы используете это, вектор так же быстро, как массив.
Так что нет, вы не разрушили миф о производительности. Но вы показали, что это верно только в том случае, если вы используете вектор оптимально, что тоже довольно неплохо. :)
С другой стороны, это действительно простое использование, которое оказывается самым быстрым. Если вы сравните мой фрагмент кода (единственную строку) с ответом Джона Кугельмана, содержащим кучи и кучу настроек и оптимизаций, которые до сих пор не совсем устраняют разницу в производительности, то становится ясно, что
vector
все-таки довольно умно разработан. Вам не нужно прыгать через обручи, чтобы получить скорость, равную массиву. Наоборот, вы должны использовать самое простое решение.источник
new[]
выполняет те же конструкции по умолчанию, чтоvector.resize()
и при этом, но гораздо быстрее.new[]
+ внутренний цикл должен быть такой же скорости, как иvector.resize()
+ внутренний цикл, но это не так, он почти в два раза быстрее.malloc
который ничего не инициализирует и не создает, поэтому он, по сути, является однопроходным алгоритмом, как и мойvector
пример. А что касаетсяnew[]
ответа, очевидно, что оба требуют двух проходов, но в этомnew[]
случае компилятор может оптимизировать эти дополнительные издержки, чего не происходит в этомvector
случае. Но я не понимаю, почему интересно, что происходит в неоптимальных случаях. Если вы заботитесь о производительности, вы не пишете такой код.vector::resize()
чтобы дать мне много памяти, не тратя время на вызов бесполезных конструкторов.malloc
который не выполняет инициализацию, но это не будет работать в C ++ с не POD-типами. Так что в общем случае массив C ++ будет таким же плохим. Возможно, вопрос в том, что если вы собираетесь часто выполнять этот процесс, не будете ли вы использовать один и тот же массив / вектор? И если вы сделаете это, то вы заплатите стоимость «бесполезных конструкторов» только один раз, в самом начале. Фактически блиттинг так же быстр в конце концов.Вряд ли это было честное сравнение, когда я впервые посмотрел на ваш код; Я определенно думал, что ты не сравниваешь яблоки с яблоками. Поэтому я подумал, что давайте вызовем конструкторы и деструкторы во всех тестах; а потом сравни.
Я думал, что при такой настройке они должны быть точно такими же. Оказывается, я был не прав.
Так почему же произошла эта 30% потеря производительности? STL имеет все в заголовках, поэтому компилятор должен был понимать все, что требовалось.
Я думал, что именно так цикл инициализирует все значения конструктору по умолчанию. Итак, я выполнил тест:
Результаты были, как я и подозревал:
Это явно источник замедления, тот факт, что вектор использует конструктор копирования для инициализации элементов из созданного по умолчанию объекта.
Это означает, что при построении вектора происходит следующий порядок псевдоопераций:
Который, благодаря неявному конструктору копирования, созданному компилятором, расширен до следующего:
Таким образом , по умолчанию
Pixel
остается не-инициализирован, в то время как остальные инициализируются с по умолчаниюPixel
«s не-инициализируются значения.По сравнению с альтернативной ситуацией с
New[]
/Delete[]
:Все они оставлены на их неинициализированные значения и без двойной итерации по последовательности.
Вооружившись этой информацией, как мы можем ее проверить? Давайте попробуем переписать неявный конструктор копирования.
И результаты?
Итак, подведем итог: если вы часто создаете сотни векторов, переосмыслите свой алгоритм .
В любом случае реализация STL не медленнее по неизвестной причине, она просто делает то, что вы просите; надеюсь, что вы знаете лучше.
источник
Попробуйте отключить проверенные итераторы и построить в режиме релиза. Вы не должны видеть большую разницу в производительности.
источник
#define _SECURE_SCL 0
. Это сделалоUseVector
где-то около 4 секунд (похоже наgcc
ниже), но все же это в два раза медленнее.-O3
._HAS_ITERATOR_DEBUGGING
он отключен в сборке релиза: msdn.microsoft.com/en-us/library/aa985939(VS.80).aspxВ GNU STL (и других), заданный по
vector<T>(n)
умолчанию, создается прототип объектаT()
- компилятор оптимизирует пустой конструктор, но затем копия того мусора, который оказался в адресах памяти, теперь зарезервированных для объекта, берется STL.__uninitialized_fill_n_aux
, что циклы, заполняющие копии этого объекта в качестве значений по умолчанию в векторе. Итак, «мой» STL - это не циклическое конструирование, а конструирование затем циклическое копирование. Это противоречит интуиции, но я должен был вспомнить, как я прокомментировал недавний вопрос о стековом потоке по этому поводу: конструкция / копия может быть более эффективной для объектов с подсчетом ссылок и т. Д.Так:
или
это - на многих реализациях STL - что-то вроде:
Проблема заключается в том, что текущее поколение оптимизаторов компилятора, похоже, не работает из-за понимания, что temp является неинициализированным мусором, и не может оптимизировать вызовы цикла и вызовы конструктора копирования по умолчанию. Вы могли бы с уверенностью утверждать, что компиляторы абсолютно не должны оптимизировать это, поскольку программист, пишущий выше, имеет разумное ожидание, что все объекты будут идентичны после цикла, даже если мусор (обычные предостережения о «тождественном» / operator == vs memcmp / operator = и т.д. применяются). Нельзя ожидать, что компилятор будет иметь какое-либо дополнительное понимание более широкого контекста std :: vector <> или более позднего использования данных, которое предполагает безопасную оптимизацию.
Это можно противопоставить более очевидной прямой реализации:
Что мы можем ожидать от компилятора для оптимизации.
Чтобы быть немного более явным в обосновании этого аспекта поведения вектора, рассмотрим:
Очевидно, что это большая разница, если мы создадим 10000 независимых объектов против 10000 ссылок на одни и те же данные. Существует разумный аргумент, что преимущество защиты случайных пользователей C ++ от случайного выполнения чего-либо столь дорогостоящего перевешивает очень небольшую реальную стоимость трудно оптимизируемой конструкции копирования.
ОРИГИНАЛЬНЫЙ ОТВЕТ (для справки / понимания комментариев): Нет шансов. вектор так же быстр, как массив, по крайней мере, если вы разумно резервируете пространство. ...
источник
Ответ Мартина Йорка беспокоит меня, потому что это похоже на попытку отмахнуться от проблемы инициализации под ковром. Но он прав, если определит избыточную конструкцию по умолчанию как источник проблем с производительностью.
[РЕДАКТИРОВАТЬ: ответ Мартина больше не предлагает изменить конструктор по умолчанию.]
Для решения ближайшей проблемы вы, конечно, могли бы
vector<Pixel>
вместо этого вызвать 2-параметрическую версию ctor:Это работает, если вы хотите инициализировать с постоянным значением, что является распространенным случаем. Но более общая проблема заключается в следующем: как эффективно инициализировать что-то более сложное, чем постоянное значение?
Для этого вы можете использовать
back_insert_iterator
адаптер-итератор. Вот пример с векторомint
s, хотя общая идея работает так же хорошо дляPixel
s:В качестве альтернативы вы можете использовать
copy()
илиtransform()
вместоgenerate_n()
.Недостатком является то, что логику для конструирования начальных значений необходимо перенести в отдельный класс, что менее удобно, чем иметь его на месте (хотя лямбда-выражения в C ++ 1x делают это намного приятнее). Кроме того, я ожидаю, что это все равно будет не так быстро, как на
malloc()
основе не-STL версии, но я ожидаю, что это будет близко, так как он только делает одну конструкцию для каждого элемента.источник
Векторные дополнительно называют конструкторами Pixel.
Каждый из них вызывает почти миллион циклов, которые вы рассчитываете.
edit: тогда есть внешний цикл 1 ... 1000, так что сделайте, чтобы миллиард ctor звонил!
edit 2: было бы интересно увидеть разборку для случая UseArray. Оптимизатор может оптимизировать все это, так как он не имеет никакого эффекта, кроме горения процессора.
источник
Вот как работает
push_back
метод в векторе:После вызова
push_back
X предметов:Повторение. Если вы не в
reserving
космосе, это определенно будет медленнее. Более того, если копировать предмет дорого, то «push_back» как будто съест вас заживо.Что касается
vector
вещи против массива, мне придется согласиться с другими людьми. Запустите в выпуске, включите оптимизацию и добавьте еще несколько флагов, чтобы дружелюбные люди в Microsoft не поняли вас.Еще одна вещь, если вам не нужно изменять размер, используйте Boost.Array.
источник
reserve
как я должен.push_back
амортизировала постоянное время. Похоже, вы описываете процесс O (N). (Шаги 1 и 3 кажутся совершенно неуместными.) Чтоpush_back
замедляет OP, так это проверка диапазона, чтобы увидеть, должно ли произойти перераспределение, обновление указателей, проверка NULL внутри размещенияnew
и другие мелочи, которые обычно заглушаются фактическая работа программы.reserve
что он все еще должен делать эту проверку (нужно ли это перераспределять) на каждомpush_back
.vector
выполняет функцию изменения размера, это просто «волшебство». Здесь, позвольте мне прояснить это немного больше.Некоторые данные профилировщика (пиксель выровнен до 32 бит):
вздор
В
allocator
:vector
:массив
Большая часть накладных расходов находится в конструкторе копирования. Например,
Он имеет ту же производительность, что и массив.
источник
pixels.size()
будет сломано.Мой ноутбук - Lenova G770 (4 ГБ оперативной памяти).
ОС Windows 7 64-битная (та, что с ноутбуком)
Компилятор MinGW 4.6.1.
Средой разработки является Code :: Blocks .
Я тестирую исходники первого поста.
Результаты
O2 оптимизация
Использование массива завершено за 2,841 секунды
UseVector завершен за 2,548 секунды
UseVectorPushBack завершено за 11,95 секунд
Все завершено за 17,342 секунды
системная пауза
O3 оптимизация
Использование массива завершено за 1.452 секунды
UseVector завершен за 2,514 секунды
UseVectorPushBack завершено за 12,967 секунд
Все закончено за 16,937 секунд
Похоже, производительность вектора хуже при оптимизации O3.
Если вы измените цикл на
Скорость массива и вектора под O2 и O3 практически одинаковы.
источник
Лучший тест (я думаю ...), компилятор за счет оптимизации может изменить код, потому что результаты распределенных векторов / массивов нигде не используются. Полученные результаты:
Составитель:
ЦПУ:
И код:
источник
Я провел несколько обширных тестов, которые хотел провести некоторое время. Могу также поделиться этим.
Это моя машина с двойной загрузкой i7-3770, Ram 16 ГБ, x86_64, на Windows 8.1 и на Ubuntu 16.04. Более подробная информация и выводы, замечания ниже. Протестировано как MSVS 2017, так и g ++ (как на Windows, так и на Linux).
Тестовая программа
Полученные результаты
Ноты
std::sort()
тоже проводил тесты (вы можете видеть это закомментировано), но позже удалил их, потому что не было значительных относительных различий.Мои выводы и замечания
std::array
временных вариаций между последовательными запусками, в то время как другие, особенно структуры std :: data, сильно отличались по сравнениюstd::array
и массивы в стиле c быстрее на Windows без оптимизациирешение суда
Конечно, это код для оптимизированной сборки. И так как вопрос был о
std::vector
да то да! Много! медленнее, чем простые массивы (оптимизированные / неоптимизированные). Но когда вы делаете тест, вы, естественно, хотите создавать оптимизированный код.Звезда шоу для меня хоть и была
std::array
.источник
При правильных настройках векторы и массивы могут генерировать идентичные ассемблеры . В этих случаях они, конечно, имеют одинаковую скорость, потому что вы получаете один и тот же исполняемый файл в любом случае.
источник
Кстати, замедление просмотра в классах с использованием вектора также происходит со стандартными типами, такими как int. Вот многопоточный код:
Поведение из кода показывает, что создание экземпляра вектора является самой длинной частью кода. Как только вы пройдете через горлышко бутылки. Остальная часть кода работает очень быстро. Это верно независимо от того, сколько потоков вы используете.
Кстати игнорируйте абсолютно безумное количество включений. Я использовал этот код для тестирования проекта, поэтому число включений продолжает расти.
источник
Я просто хочу упомянуть, что vector (и smart_ptr) - это просто добавление тонкого слоя поверх необработанных массивов (и необработанных указателей). И на самом деле время доступа вектора в непрерывной памяти быстрее, чем в массиве. Следующий код показывает результат инициализации и доступа к вектору и массиву.
Выход:
Так что скорость будет почти такой же, если вы используете ее правильно. (как другие упоминали, используя Reserve () или Resize ()).
источник
Ну, потому что vector :: resize () выполняет намного больше обработки, чем обычное выделение памяти (по malloc).
Попробуйте установить точку останова в конструкторе копирования (определите ее так, чтобы вы могли использовать точку останова!), И тогда уйдет дополнительное время обработки.
источник
Я должен сказать, что я не эксперт в C ++. Но чтобы добавить некоторые результаты экспериментов:
компилировать: gcc-6.2.0 / bin / g ++ -O3 -std = c ++ 14 vector.cpp
машина:
ОПЕРАЦИОННЫЕ СИСТЕМЫ:
Вывод:
Здесь единственное, что мне кажется странным, - это производительность UseFillConstructor по сравнению с UseConstructor.
Код:
Таким образом, дополнительное «значение» значительно снижает производительность, что, я думаю, связано с многократным вызовом конструктора копирования. Но...
Обобщение:
Вывод:
Так что в этом случае оптимизация gcc очень важна, но она не может вам помочь, если значение предоставлено по умолчанию. Это против моего обучения на самом деле. Надеюсь, это поможет новому программисту выбрать формат векторной инициализации.
источник
Кажется, это зависит от флагов компилятора. Вот эталонный код:
Различные флаги оптимизации дают разные ответы:
Точные результаты будут отличаться, но это довольно типично для моей машины.
источник
По моему опыту, иногда, просто иногда,
vector<int>
может быть во много раз медленнее, чемint[]
. Следует помнить, что векторы векторов очень разныеint[][]
. Поскольку элементы, вероятно, не являются смежными в памяти. Это означает, что вы можете изменять размеры разных векторов внутри основного, но ЦП может не иметь возможности кэшировать элементы так же, как и в случаеint[][]
.источник