2012-01-13 1 views
114

Какой самый быстрый способ сбросить каждое значение std::vector<int> до 0 и сохранить начальный размер вектора?Самый быстрый способ сбросить каждое значение std :: vector <int> до 0

A для цикла с оператором []?

+3

[станд :: заливка] (http://www.cplusplus.com/reference/algorithm/fill/) –

+0

"быстрый", как в производительности? Или как проще всего реализовать/поддерживать? – TheGeneral

ответ

218
std::fill(v.begin(), v.end(), 0); 
+30

Глядя на сборку, gcc на самом деле разворачивает этот цикл с использованием регистров mmx для дампа в 16 байт за раз, пока он не приблизится к концу. Я бы сказал, что это довольно быстро. Версия memset переходит к memset, и я предполагаю, что это так же быстро. Я бы использовал ваш метод. – Omnifarious

+0

Но прыжок в memset - это одна команда, поэтому ее использование приведет к меньшему двоичному размеру. –

+0

Это не совсем то, о чем попросил ОП, а просто переназначение вашего вектора на новый один размер ('v = std :: vector (vec_size, 0)') кажется немного быстрее, чем 'fill' на моей машине –

12

Если это просто вектор целых чисел, я бы первым попробовать:

memset(&my_vector[0], 0, my_vector.size() * sizeof my_vector[0]); 

Это не очень C++, так что я уверен, что кто-то даст правильный способ сделать это. :)

+2

Поскольку стандарт (2003 TC1) гарантирует, что std :: vector смежен в памяти, это должно быть хорошо. Если ваша библиотека C++ не соответствует TC1 2003 года, не используйте это. – Mario

+2

@Mario: Я бы не опубликовал это, если бы это не было правдой и считалось общеизвестным, конечно. :) Но спасибо. – unwind

+1

Я проверил сборку. Метод ':: std :: fill' расширяется до того, что быстро зашифровывается, хотя немного на стороне кода, так как он все встроен. Я все равно буду использовать его, потому что его гораздо приятнее читать. – Omnifarious

2

попробовать

std::fill 

, а также

std::size siz = vec.size(); 
//no memory allocating 
vec.resize(0); 
vec.resize(siz, 0); 
+0

Изменение размера очень хорошее – Nick

109

Как всегда, когда вы спрашиваете о быстро: Измерить! Использование методов выше (на Mac с помощью Clang):

Method  | executable size | Time Taken (in sec) | 
      | -O0 | -O3 | -O0  | -O3  | 
------------|---------|---------|-----------|----------| 
1. memset | 17 kB | 8.6 kB | 0.125  | 0.124 | 
2. fill  | 19 kB | 8.6 kB | 13.4  | 0.124 | 
3. manual | 19 kB | 8.6 kB | 14.5  | 0.124 | 
4. assign | 24 kB | 9.0 kB | 1.9  | 0.591 | 

используя 100000 итераций на векторе 10000 Интс.

Edit: Если changeing этого числа правдоподобны изменения результирующие раз вы можете иметь некоторые доверия (не так хорошо, как инспектирование окончательного кода сборки), что искусственный тест не был оптимизирован прочь полностью. Разумеется, лучше справляться с работой в реальных условиях. конец Edit

для справки используемый код:

#include <vector> 

#define TEST_METHOD 1 
const size_t TEST_ITERATIONS = 100000; 
const size_t TEST_ARRAY_SIZE = 10000; 

int main(int argc, char** argv) { 

    std::vector<int> v(TEST_ARRAY_SIZE, 0); 

    for(size_t i = 0; i < TEST_ITERATIONS; ++i) { 
    #if TEST_METHOD == 1 
     memset(&v[0], 0, v.size() * sizeof v[0]); 
    #elif TEST_METHOD == 2 
     std::fill(v.begin(), v.end(), 0); 
    #elif TEST_METHOD == 3 
     for (std::vector<int>::iterator it=v.begin(), end=v.end(); it!=end; ++it) { 
     *it = 0; 
     } 
    #elif TEST_METHOD == 4 
     v.assign(v.size(),0); 
    #endif 
    } 

    return EXIT_SUCCESS; 
} 

Вывод: использование std::fill (потому что, как уже говорили другие его наиболее идиоматических)!

+2

+1. Этот конкретный ориентир не является окончательным, но это абсолютно правильно, вы должны написать тест производительности альтернатив, поскольку они будут фактически использоваться. Если нет разницы в производительности, используйте тот, который является самым простым источником. –

+1

«... не убедительно ...» ИМО эта непоследовательность сама по себе уже является хорошим моментом для выполнения тестов, чаще всего Оптимизатор уже делает очень хорошую работу для тех ситуаций, о которых спрашивал ОП. И я бы изменил ваше последнее предложение следующим образом: «Если нет ** существенной разницы в производительности ...» –

+1

«Неконкретно» Я имел в виду, что только потому, что они были одинаковой скоростью в этой программе, это не обязательно означает все они будут одинаковой скоростью в программе опроса. Помимо всего прочего, вам нужно быть уверенным, что память была фактически обнулена - возможно, оптимизатор был достаточно умен, чтобы обмануть тест. Но так как у вас нет программы опроса, это не провал этого ответа :-) И вы абсолютно правы, очень легко тратить время, мучительно на выбор, который фактически не имеет никакого значения вообще (или незначительная разница) после оптимизации. –

18

Как насчет функции assign?

some_vector.assign(some_vector.size(), 0); 
+0

OP хотел сбросить существующие значения, но ваш ответ лучше, если вы хотите изменить размер _ и_ сбросить значения. Благодаря! –

0

У меня был один и тот же вопрос, но о довольно короткий vector<bool> (AFAIK стандарт позволяет реализовать его внутри по-другому, чем просто непрерывный массив булевых элементов). Поэтому я повторил слегка измененные тесты Фабио Фракасси. Результаты таковы (время в секундах):

  -O0  -O3 
     -------- -------- 
memset  0.666  1.045 
fill  19.357  1.066 
iterator 67.368  1.043 
assign 17.975  0.530 
for i  22.610  1.004 

Таким образом, очевидно для этих размеров, vector<bool>::assign() быстрее. Код, используемый для испытаний:

#include <vector> 
#include <cstring> 
#include <cstdlib> 

#define TEST_METHOD 5 
const size_t TEST_ITERATIONS = 34359738; 
const size_t TEST_ARRAY_SIZE = 200; 

using namespace std; 

int main(int argc, char** argv) { 

    std::vector<int> v(TEST_ARRAY_SIZE, 0); 

    for(size_t i = 0; i < TEST_ITERATIONS; ++i) { 
#if TEST_METHOD == 1 
     memset(&v[0], false, v.size() * sizeof v[0]); 
#elif TEST_METHOD == 2 
     std::fill(v.begin(), v.end(), false); 
    #elif TEST_METHOD == 3 
     for (std::vector<int>::iterator it=v.begin(), end=v.end(); it!=end; ++it) { 
      *it = 0; 
     } 
    #elif TEST_METHOD == 4 
     v.assign(v.size(),false); 
    #elif TEST_METHOD == 5 
     for (size_t i = 0; i < TEST_ARRAY_SIZE; i++) { 
      v[i] = false; 
     } 
#endif 
    } 

    return EXIT_SUCCESS; 
} 

Я использовал GCC 7.2.0 компилятора на Ubuntu 17.10. Командная строка для компиляции:

g++ -std=c++11 -O0 main.cpp 
g++ -std=c++11 -O3 main.cpp 
Смежные вопросы