std :: vector <int>의 모든 값을 0으로 재설정하는 가장 빠른 방법


답변:


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

48
어셈블리 출력을 살펴보면 gcc는 실제로이 루프를 mmx 레지스터를 사용하여 끝까지 가까워 질 때까지 한 번에 16 바이트로 덤프합니다. 나는 그것이 매우 빠르다고 말합니다. memset 버전은 memset으로 점프합니다. 나는 당신의 방법을 사용할 것입니다.
Omnifarious

그러나 memset으로 점프하는 것은 단일 명령이므로이 명령을 사용하면 이진 크기가 더 작아집니다.
Alexander Shishenko

2
이것은 정확히 OP가 요구 한 것이 아니라, 같은 크기 ( v = std::vector<int>(vec_size,0)) 의 새로운 벡터로 벡터를 다시 할당하는 것만 fill으로 내 컴퓨터 보다 약간 더 빠릅니다.
Yibo Yang

1
이것은 가장 관용적 인 방법으로 사용하는 것보다 관용적 assign입니다.
alfC

1
새 벡터에 할당하면 힙 할당이 수행됩니까? 그런 다음 기존 벡터의 할당을 버리십시오. 나는 memset et al보다 느리다는 것을 알 수 있었다
Conrad Jones

150

당신이 가장 빠른 것에 대해 물을 때 항상 그렇듯이 : 측정! 위의 방법 사용 (Clang을 사용하는 Mac에서) :

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    |

10000 ints의 벡터에서 100000 반복을 사용합니다.

편집 : 그럴듯하게이 숫자를 changeing하는 결과 시간을 변경하면 당신이 가질 수있는 약간의 자신감을 (안 최종 어셈블리 코드 검사 좋은 등) 인공 벤치 마크는 완전히 멀리 최적화되지 않았 음을. 물론 실제 상황에서 성능을 망쳐 놓는 것이 가장 좋습니다. 편집 종료

사용 된 코드를 참조하십시오 :

#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. 이 특정 벤치 마크는 결정적이지는 않지만 요점은 정확합니다. 대안이 실제로 사용될 때 성능 테스트를 작성해야합니다. 성능 차이가없는 경우 가장 간단한 소스를 사용하십시오.
Steve Jessop

3
"... 결정적이지 않다 ..."IMO이 결론은 그 자체로 이미 벤치 마크를 수행하기에 좋은 포인트이며, 종종 옵티마이 저가 OP가 요청한 상황에 대해 아주 좋은 일을하지 않는 경우가 많습니다. "어떤이 없다면 내가 읽을 수있는 마지막 문장을 수정하는 것 상당한 성능 차이 ..."
파비오 Fracassi

4
벤치 마크에 Nonius 를 사용하여 업데이트 : clang3.6-libc ++-c ++ 1y-O3 , gcc4.9-c ++ 1y-O3 gcc5-c ++ 1y-O3 - TL; DR : assign작은 용량을 제외하고 속도가 느림 에 libc++. CODE coliru / paste
sehe

2
또한 최적화없이 속도에 관심이 있다면 (일부 팀이하는 '디버그'모드로 배포하는 경우 그럴듯 할 수 있습니다) fill끔찍한 것처럼 보입니다. 그것은이다 , 2 차의 크기에 느린이 테스트한다.
Kyle Strand

5
@KyleStrand : 채우기가 끔찍한 것이 아니며 템플릿이며 번역 단위 내에서 -O0으로 코드가 생성됩니다. memset을 사용할 때는 -O3으로 컴파일 된 libc 코드를 사용하고 있습니다 (-O0으로 코드를 컴파일 할 때도). 디버그 속도에 관심이 있고 템플릿을 사용하는 경우 -O3
Tic

25

방법에 대한 assign멤버 함수?

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

2
OP는 기존 값을 재설정하려고했지만 값의 크기를 조정 하고 재설정 하려는 경우 대답이 더 좋습니다 . 감사!

15

정수로 구성된 벡터라면 먼저 시도해보십시오.

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

그것은 C ++이 아니기 때문에 누군가가 이것을하는 올바른 방법을 제공 할 것이라고 확신합니다. :)


3
표준 (2003 TC1)은 std :: vector가 메모리에서 연속적임을 보증하므로, 이는 괜찮습니다. c ++ 라이브러리가 2003 TC1을 준수하지 않으면 이것을 사용하지 마십시오.
Mario

2
@Mario : 물론 이것이 사실이고 잘 알려져 있지 않다면 이것을 게시하지 않았을 것입니다. :) 그러나 감사합니다.
언 와인드

1
어셈블리를 확인했습니다. 이 ::std::fill방법은 코드가 너무 빠르지 만 약간 인라인이 있기 때문에 다소 빠른 속도로 확장됩니다. 나는 그것을 읽는 것이 훨씬 좋기 때문에 여전히 그것을 사용합니다.
Omnifarious

4
벡터가 비어 있는지 확인 하고이 경우 아무것도하지 않는 것이 좋습니다. 빈 벡터에 대해 & buf [0]을 계산하면 STL 코드에서 어설 션이 생성 될 수 있습니다.
Sergey

4

시험

std::fill

그리고 또한

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

크기 조정은 매우 좋습니다
Nick

3

나는 똑같은 질문을했지만 다소 짧았습니다 vector<bool>(faik 표준은 부울 요소의 연속 배열과 내부적으로 다르게 구현할 수 있습니다). 따라서 나는 Fabio Fracassi에 의해 약간 수정 된 테스트를 반복했다. 결과는 다음과 같습니다 (시간, 초).

            -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;
}

우분투 17.10에서 GCC 7.2.0 컴파일러를 사용했습니다. 컴파일 명령 줄 :

g++ -std=c++11 -O0 main.cpp
g++ -std=c++11 -O3 main.cpp
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.