Cách nhanh nhất để đặt lại mọi giá trị của std :: vector <int> thành 0


198

Cách nhanh nhất để đặt lại mọi giá trị từ std::vector<int>0 đến 0 và giữ kích thước ban đầu của vectơ là gì?

Một vòng lặp for với toán tử []?



1
"Nhanh nhất" như trong hiệu suất? Hoặc như dễ dàng nhất để thực hiện / duy trì?
TheGeneral

Câu trả lời:


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

48
Nhìn vào đầu ra lắp ráp, gcc thực sự hủy bỏ vòng lặp này bằng cách sử dụng các thanh ghi mmx để kết xuất 16 byte mỗi lần cho đến khi gần hết. Tôi muốn nói là khá nhanh. Phiên bản bộ nhớ chuyển sang bộ nhớ, mà tôi đoán là nhanh như vậy. Tôi sẽ sử dụng phương pháp của bạn.
Omnifarious

Nhưng, nhảy vào bộ nhớ là một hướng dẫn duy nhất, vì vậy sử dụng nó sẽ dẫn đến kích thước nhị phân nhỏ hơn.
Alexander Shishenko

2
Đây không phải là chính xác những gì OP yêu cầu, mà chỉ đơn giản là gán lại vectơ của bạn cho một cái mới có cùng kích thước ( v = std::vector<int>(vec_size,0)) có vẻ nhanh hơn một chút so với filltrên máy của tôi
Yibo Yang

1
Đây là cách làm thành ngữ nhất, thành ngữ hơn là sử dụng assign.
alfC

1
việc gán nó cho một vectơ mới để phân bổ heap? và sau đó loại bỏ phân bổ của vectơ hiện có? Tôi có thể thấy rằng chậm hơn so với memset et al
Conrad Jones

150

Như mọi khi bạn hỏi về nhanh nhất: Đo lường! Sử dụng các Phương thức trên (trên máy Mac sử dụng 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    |

sử dụng 100000 lần lặp trên một vectơ 10000 ints.

Chỉnh sửa: Nếu changeing số này plausibly thay đổi lần kết quả bạn có thể có một số niềm tin (không tốt như kiểm tra mã lắp ráp cuối cùng) mà benchmark nhân tạo chưa được tối ưu hóa đi hoàn toàn. Tất nhiên là tốt nhất để làm hỏng hiệu suất trong điều kiện thực tế. kết thúc chỉnh sửa

để tham khảo mã được sử dụng:

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

Kết luận: sử dụng std::fill(bởi vì, như những người khác đã nói thành ngữ nhất của nó)!


3
+1. Điểm chuẩn cụ thể này không phải là kết luận, nhưng quan điểm là hoàn toàn chính xác, bạn nên viết một bài kiểm tra hiệu suất của các lựa chọn thay thế vì chúng thực sự sẽ được sử dụng. Nếu không có sự khác biệt về hiệu suất thì hãy sử dụng cái nào là nguồn đơn giản nhất.
Steve Jessop

3
"... không thể kết luận ..." Bản thân IMO tính không phù hợp này đã là một điểm tốt để thực hiện các điểm chuẩn, thường xuyên hơn là Trình tối ưu hóa đã làm rất tốt cho các tình huống mà OP đã hỏi. Và tôi sẽ sửa đổi câu cuối cùng của bạn thành "Nếu không có sự khác biệt đáng kể về hiệu suất ..."
Fabio Fracassi

4
CẬP NHẬT Sử dụng Nonius cho điểm chuẩn: clang3.6-libc ++ - c ++ 1y-O3 , gcc4.9-c ++ 1y-O3gcc5-c ++ 1y-O3 - TL; DR : assignchậm hơn, ngoại trừ dung lượng nhỏ trên libc++. MÃ coliru / dán
sehe

2
Ngoài ra, wow, nếu bạn quan tâm đến tốc độ mà không tối ưu hóa (có thể hợp lý nếu bạn đang triển khai ở chế độ 'gỡ lỗi', điều mà một số đội làm), có fillvẻ rất tệ. Đó là hai lệnh độ lớn chậm hơn trong thử nghiệm này.
Kyle Strand

5
@KyleStrand: Không phải là điền là khủng khiếp, nó là một mẫu và mã được tạo với -O0 bên trong đơn vị dịch thuật của bạn. Khi bạn sử dụng bộ nhớ, bạn đang sử dụng mã libc được biên dịch bằng -O3 (ngay cả khi bạn biên dịch mã của mình với -O0). Nếu bạn quan tâm đến tốc độ trong gỡ lỗi và bạn sử dụng các mẫu, bạn sẽ phải sử dụng tính năng khởi tạo mẫu rõ ràng trong một tệp riêng mà bạn biên dịch với -O3
Tic

25

Làm thế nào về assignchức năng thành viên?

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

2
OP muốn đặt lại các giá trị hiện có, nhưng câu trả lời của bạn sẽ tốt hơn khi muốn thay đổi kích thước đặt lại các giá trị. Cảm ơn!

15

Nếu đó chỉ là một vectơ số nguyên, trước tiên tôi nên thử:

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

Nó không phải là C ++, vì vậy tôi chắc chắn ai đó sẽ cung cấp cách thức phù hợp để làm việc này. :)


3
Do tiêu chuẩn (2003 TC1) đảm bảo rằng một vectơ std :: tiếp giáp trong bộ nhớ, nên điều này sẽ ổn. Nếu thư viện c ++ của bạn không phù hợp với TC1 2003, thì đừng sử dụng thư viện này.
Mario

2
@Mario: Tôi sẽ không đăng bài này trừ khi đó là sự thật và tất nhiên là được biết đến. :) Nhưng cảm ơn.
thư giãn

1
Tôi đã kiểm tra lắp ráp. Các ::std::fillphương pháp mở rộng để cái gì đó khá darned nhanh, mặc dù một chút về phía mã bloaty vì nó là tất cả nội tuyến. Tôi vẫn sẽ sử dụng nó bởi vì nó dễ đọc hơn nhiều.
Omnifarious

4
Bạn nên thêm kiểm tra xem vector có trống không và không làm gì trong trường hợp này. Tính toán & buf [0] cho vectơ trống có thể tạo các xác nhận trong mã STL.
Serge

4

thử

std::fill

và cũng

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

thay đổi kích thước là rất tốt
Nick

3

Tôi đã có cùng một câu hỏi nhưng về khá ngắn vector<bool>(afaik tiêu chuẩn cho phép thực hiện nó bên trong khác với chỉ một mảng các yếu tố boolean liên tục). Do đó tôi lặp lại các bài kiểm tra sửa đổi một chút của Fabio Fracassi. Kết quả như sau (lần, tính bằng giây):

            -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

Vì vậy, rõ ràng cho các kích thước, vector<bool>::assign()là nhanh hơn. Mã được sử dụng để kiểm tra:

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

Tôi đã sử dụng trình biên dịch GCC 7.2.0 trên Ubuntu 17.10. Dòng lệnh để biên dịch:

g++ -std=c++11 -O0 main.cpp
g++ -std=c++11 -O3 main.cpp
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.