Другие ответы уже охватывали теорию, но я хотел бы добавить также некоторые цифры, чтобы понять, что происходит.
Однажды я написал небольшой набор тестов для сравнения std::vector
push_back
, reserve
+ push_back
, размер в конструкторе (который должен совпадать с resize
) + operator[]
и необработанные динамические массивы;Вот результаты для 10000000 int
с:
matteo@teoubuntu:~/cpp/vectorbenchmark2$ ./vectorbenchmark2
Insert the number of elements: 10000000
Insert the number of iterations: 100
Minimum allocation for each benchmark 40000000 bytes.
Starting benchmark std::vector<int> (push_back/iterator, without reserve)... benchmark completed.
Results: 173.1 +/- 2.4 ms
Starting benchmark std::vector<int> (push_back/iterator, with reserve)... benchmark completed.
Results: 122.17 +/- 0.57 ms
Starting benchmark std::vector<int> (sized with constructor/operator[])... benchmark completed.
Results: 115.95 +/- 0.66 ms
Starting benchmark new int[]... benchmark completed.
Results: 121.33 +/- 0.84 ms
Starting benchmark malloc+memset... benchmark completed.
Results: 123.9 +/- 4.3 ms
Starting benchmark malloc... benchmark completed.
Results: 117.7 +/- 2.3 ms
Starting benchmark std::list<int>... benchmark completed.
Results: 552 +/- 35 ms
Каждый тест состоит из выделения структуры данных, заполнения ее случайными числами и перечитывания всего в переменной volatile
.Тесты выполняются столько раз, сколько указано во втором запрашиваемом параметре (в этом цикле я поставил 100);время измеряется с gettimeofday
.Отображаемые результаты представляют собой среднее значение и стандартное отклонение, рассчитанное для всех времен каждой категории.Код для всего этого доступен здесь .
Какие результаты мы можем извлечь из этих данных?
- Первый очевидный результат заключается в том, что
push_back
Без предварительного выделения самый медленный метод: в этих условиях он примерно на 41% медленнее, чем push_back
с предварительным распределением.Таким образом, если время является критическим фактором и вычисление того, насколько большим будет ваш vector
, не составит труда, reserve
может быть действительно удобным.Очевидно, что если ваше выделение + заполнение в целом занимает ~ 10 мс, даже сокращение его на 40% не будет действительно замечено;как обычно, оптимизируйте там, где это имеет смысл (т. е. на узких местах). - еще один интересный результат заключается в том, что инициализация размера массива (вместо «прозрачного» предварительного выделения с
reserve
) выполняется еще быстрее;Я думаю, что это потому, что мы не вызываем push_back
(который должен обновлять vector
«официальный размер») в узком цикле;вместо этого operator[]
почти ничего не делает, кроме суммы указателей, и легко вставляется.Возможно, с помощью итераторов (которые в случае vector
являются в основном указателями) можно получить что-то большее. - Интересно, что единственный реальный претендент с готовностью -
resize
d vector
- это просто malloc
, который не предлагает никаких наворотов и не подходит для не POD-типов (и все же в этом самом тесте результаты немного хуже).Замечательно, что vector
может быть , что эффективным, сохраняя при этом свои преимущества (я говорю с вами, люди, которые распространяют C-style mallocs
вокруг для предполагаемого увеличения производительности!) std::list
(введено для сравнения) - самый медленный из них - но мы уже знали это :)
Очевидно, YMMV;Прежде всего, это результаты для конкретной архитектуры с конкретным компилятором и его конкретной реализацией STL (а именно, g ++ 4.4.5).Затем я думаю, что использование «сложных» типов (с конструкторами, конструкторами копирования и т. Д.) Может изменить положения методов reserve
/ resize
(resize
необходимо для создания всех объектов в vector
, в то время какЯ не думаю, что reserve
делает).Некоторые изменения могут также возникнуть из-за изменения используемого типа примитива (int
против long
против short
и т. Д.).
Более того, между всеми тестами, кроме первого и последнего, разница довольно мала;оно, безусловно, значительно больше стандартного отклонения, но я думаю, что даже небольшие изменения в стандартной библиотеке могут изменить некоторые их относительные результаты.Кроме того, изменение количества элементов также должно иметь заметные эффекты, так как эти времена являются суммами различных операций big-O, поэтому увеличение или уменьшение количества элементов может изменить преобладающее добавление к сумме.
Условия тестирования (скучный материал)
AMD Phenom X4 955 (3,2 ГГц x4), 4 ГБ ОЗУ DDR3
Компилятор:
matteo@teoubuntu:~/cpp/vectorbenchmark2$ g++ --version
g++ (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5
Copyright (C) 2010 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
ОС/ Ядро:
matteo@teoubuntu:~/cpp/vectorbenchmark2$ uname -a
Linux teoubuntu 2.6.35-24-generic #42-Ubuntu SMP Thu Dec 2 02:41:37 UTC 2010 x86_64 GNU/Linux
Параметры компиляции:
matteo@teoubuntu:~/cpp/vectorbenchmark2$ make
g++ -O3 -Wall -Wextra -ansi -pedantic -MMD -MF BenchmarkFunctors.o.d -c -o BenchmarkFunctors.o BenchmarkFunctors.cpp
g++ -O3 -Wall -Wextra -ansi -pedantic -MMD -MF main.o.d -c -o main.o main.cpp
g++ -O3 -Wall -Wextra -ansi -pedantic -MMD -MF Utils.o.d -c -o Utils.o Utils.cpp
g++ -O3 -Wall -Wextra -ansi -pedantic BenchmarkFunctors.o main.o Utils.o -o vectorbenchmark2
Код набора тестов