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


198

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

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



1
«Самый быстрый» как по производительности? Или как проще всего внедрить / поддерживать?
Общее

Ответы:


341
std::fill(v.begin(), v.end(), 0);

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

Но переход к memset - это отдельная инструкция, поэтому ее использование приведет к уменьшению двоичного размера.
Александр Шишенко

2
это не совсем то, о чем просил OP, но просто переназначение вашего вектора на новый с таким же размером ( v = std::vector<int>(vec_size,0)) кажется немного быстрее, чем fillна моей машине
Yibo Yang

1
Это самый идиоматический способ сделать это, более идиоматический, чем использование assign.
AlfC

1
назначает ли это новому вектору распределение кучи? а затем отменить выделение существующего вектора? Я мог видеть, что это медленнее, чем memset и др.
Конрад Джонс

150

Как всегда, когда вы спрашиваете о самом быстром: Мера! Используя Методы выше (на 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 целых.

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

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

#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(потому что, как говорили другие, это идиоматично)!


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

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

4
ОБНОВЛЕНИЕ Использование Nonius для тестов производительности: clang3.6-libc ++ - c ++ 1y-O3 , gcc4.9-c ++ 1y-O3 и gcc5-c ++ 1y-O3 - TL; DR : assignмедленнее, за исключением небольших мощностей на libc++. CODE coliru / paste
сехе

2
Кроме того, ничего себе, если вы заботитесь о скорости без оптимизации (что может быть правдоподобно, если вы развертываете в режиме «отладки», что делают некоторые команды), fillвыглядит ужасно. В этом тесте он на два порядка медленнее.
Кайл Стрэнд,

5
@KyleStrand: Дело не в том, что заливка ужасна, это шаблон, и код генерируется с -O0 внутри вашего модуля перевода. Когда вы используете memset, вы используете код libc, который был скомпилирован с -O3 (даже когда вы компилируете свой код с -O0). Если вы заботитесь о скорости отладки и используете шаблоны, вам придется использовать явную реализацию шаблона в отдельном файле, который вы компилируете с -O3
Tic

25

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

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

2
ОП хотел сбросить существующие значения, но ваш ответ лучше, если вы хотите изменить размер и сбросить значения. Спасибо!

15

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

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

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


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

2
@Mario: я бы не опубликовал это, если бы это не было правдой и, конечно, предполагалось, что это хорошо известно. :) Но спасибо.
расслабиться

1
Я проверил сборку. ::std::fillМетод расширяется к чему - то , что это чертовски быстро, хотя и немного по кодовым bloaty стороне , так как это все инлайн. Я все еще использовал бы это, хотя, потому что это намного лучше читать.
Всезнающий

4
Лучше добавить проверку, если вектор пуст, и ничего не делать в этом случае. Вычисление & buf [0] для пустого вектора может генерировать утверждения в коде STL.
Сергей

4

пытаться

std::fill

а также

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

изменить размер очень приятно
Ник

3

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

            -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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.