Sử dụng mảng hoặc std :: vector trong C ++, khoảng cách hiệu năng là gì?


207

Trong khóa học C ++ của chúng tôi, họ đề nghị không sử dụng mảng C ++ cho các dự án mới nữa. Theo như tôi biết, chính Stroustroup đề nghị không sử dụng mảng. Nhưng có sự khác biệt đáng kể hiệu suất?


2
Tại sao bạn nghĩ rằng có một khoảng cách hiệu suất.
Martin York

99
Bởi vì thông thường với chức năng tốt hơn đến hiệu suất tồi tệ nhất.
tunnuz

19
Tôi đồng ý về tối ưu hóa sớm nhưng việc chọn phương pháp lưu trữ tốt hơn lên phía trước có rất nhiều ý nghĩa. Thông thường trong thế giới thực, mã cần phải được chuyển đi và sản phẩm tiếp theo được phát triển và bước tối ưu hóa không bao giờ xảy ra.
Ant

132
tôi ước mọi người sẽ ngừng la hét "tối ưu hóa sớm!" Bất cứ khi nào ai đó đang hỏi một câu hỏi đơn giản liên quan đến hiệu suất! trả lời câu hỏi và đừng chỉ NGAY LẬP TỨC giả sử mọi người đang làm bất cứ điều gì sớm.
d7samurai

4
@ d7samaurai: đồng ý, tôi chưa thấy ai dùng thửint main(int argc, const std::vector<string>& argv)
Mark K Cowan

Câu trả lời:


188

newNên tránh sử dụng mảng C ++ với (nghĩa là sử dụng mảng động). Có vấn đề bạn phải theo dõi kích thước, và bạn cần xóa chúng bằng tay và thực hiện tất cả các loại vệ sinh.

Việc sử dụng các mảng trên ngăn xếp cũng không được khuyến khích vì bạn không kiểm tra phạm vi và việc truyền mảng xung quanh sẽ làm mất bất kỳ thông tin nào về kích thước của nó (chuyển đổi mảng sang con trỏ). Bạn nên sử dụng boost::arraytrong trường hợp đó, nó bao bọc một mảng C ++ trong một lớp nhỏ và cung cấp một sizehàm và các trình vòng lặp để lặp lại nó.

Bây giờ các mảng std :: vector so với C ++ gốc (lấy từ internet):

// Comparison of assembly code generated for basic indexing, dereferencing, 
// and increment operations on vectors and arrays/pointers.

// Assembly code was generated by gcc 4.1.0 invoked with  g++ -O3 -S  on a 
// x86_64-suse-linux machine.

#include <vector>

struct S
{
  int padding;

  std::vector<int> v;
  int * p;
  std::vector<int>::iterator i;
};

int pointer_index (S & s) { return s.p[3]; }
  // movq    32(%rdi), %rax
  // movl    12(%rax), %eax
  // ret

int vector_index (S & s) { return s.v[3]; }
  // movq    8(%rdi), %rax
  // movl    12(%rax), %eax
  // ret

// Conclusion: Indexing a vector is the same damn thing as indexing a pointer.

int pointer_deref (S & s) { return *s.p; }
  // movq    32(%rdi), %rax
  // movl    (%rax), %eax
  // ret

int iterator_deref (S & s) { return *s.i; }
  // movq    40(%rdi), %rax
  // movl    (%rax), %eax
  // ret

// Conclusion: Dereferencing a vector iterator is the same damn thing 
// as dereferencing a pointer.

void pointer_increment (S & s) { ++s.p; }
  // addq    $4, 32(%rdi)
  // ret

void iterator_increment (S & s) { ++s.i; }
  // addq    $4, 40(%rdi)
  // ret

// Conclusion: Incrementing a vector iterator is the same damn thing as 
// incrementing a pointer.

Lưu ý: Nếu bạn phân bổ các mảng với newvà phân bổ các đối tượng không phải lớp (như đồng bằng int) hoặc các lớp không có hàm tạo do người dùng xác định bạn không muốn khởi tạo các phần tử của mình, sử dụng newmảng -allocated có thể có lợi thế về hiệu suất vì std::vectorkhởi tạo tất cả các phần tử giá trị mặc định (ví dụ 0 cho int) khi xây dựng (tín dụng cho @bernie để nhắc nhở tôi).


77
Ai đã phát minh ra cú pháp AT & T chết tiệt? Chỉ khi tôi biết ... :)
Mehrdad Afshari

4
Điều này không đúng với trình biên dịch Visual C ++. Nhưng đối với GCC thì đúng là như vậy.
toto

5
Vấn đề trong câu trả lời của tôi là vector mà không được chậm hơn so với correponding hoạt động con trỏ. Tất nhiên, nó cũng có thể (dễ dàng đạt được bằng cách bật chế độ gỡ lỗi) :)
Johannes Schaub - litb

18
+1 cho "Lập chỉ mục một vectơ là điều tương tự như lập chỉ mục một con trỏ." và cho các kết luận khác là tốt.
Nawaz

3
@ Piotr99 Tôi sẽ không tranh luận với bạn, nhưng khi bạn học lắp ráp sau khi học các ngôn ngữ cấp cao hơn, cú pháp Intel chỉ có ý nghĩa hơn nhiều so với một số ngược, tiền tố (số), hậu tố (hướng dẫn) và che khuất (truy cập bộ nhớ ) bản chất của cú pháp AT & T.
Cole Johnson

73

Lời mở đầu cho người tối ưu hóa vi mô

Nhớ lại:

"Các lập trình viên lãng phí rất nhiều thời gian để suy nghĩ hoặc lo lắng về tốc độ của các phần không văn bản trong chương trình của họ và những nỗ lực này có hiệu quả thực sự có tác động tiêu cực mạnh khi gỡ lỗi và bảo trì. Chúng ta nên quên đi những hiệu quả nhỏ, nói về 97% thời gian: tối ưu hóa sớm là gốc rễ của mọi tội lỗi. Tuy nhiên, chúng ta không nên bỏ qua cơ hội của mình trong 3% quan trọng đó ".

(Nhờ vào biến thái cho trích dẫn đầy đủ)

Đừng sử dụng mảng C thay vì vectơ (hoặc bất cứ thứ gì) chỉ vì bạn tin rằng nó nhanh hơn vì nó được coi là cấp thấp hơn. Bạn sẽ sai.

Sử dụng theo vectơ mặc định (hoặc thùng chứa an toàn phù hợp với nhu cầu của bạn), và sau đó nếu trình hồ sơ của bạn nói rằng đó là một vấn đề, hãy xem liệu bạn có thể tối ưu hóa nó hay không, bằng cách sử dụng thuật toán tốt hơn hoặc thay đổi vùng chứa.

Điều này nói rằng, chúng ta có thể quay trở lại câu hỏi ban đầu.

Mảng tĩnh / động?

Các lớp mảng C ++ hoạt động tốt hơn mảng C cấp thấp vì họ biết rất nhiều về bản thân và có thể trả lời các câu hỏi C mảng không thể. Họ có thể tự làm sạch. Và quan trọng hơn, chúng thường được viết bằng cách sử dụng các mẫu và / hoặc nội tuyến, điều đó có nghĩa là những gì xuất hiện rất nhiều mã trong gỡ lỗi sẽ giải quyết ít hoặc không có mã được tạo trong bản phát hành, nghĩa là không có sự khác biệt với cạnh tranh kém an toàn tích hợp của chúng.

Nói chung, nó thuộc hai loại:

Mảng động

Sử dụng một con trỏ tới mảng malloc-ed / new-ed sẽ nhanh nhất bằng phiên bản std :: vector và ít an toàn hơn (xem bài đăng của litb ).

Vì vậy, sử dụng một std :: vector.

Mảng tĩnh

Sử dụng một mảng tĩnh sẽ là tốt nhất:

  • nhanh như phiên bản std :: Array
  • và rất ít an toàn.

Vì vậy, sử dụng một mảng std :: .

Bộ nhớ chưa được khởi tạo

Đôi khi, sử dụng vectorthay vì bộ đệm thô sẽ phát sinh chi phí có thể nhìn thấy vì vectorsẽ khởi tạo bộ đệm khi xây dựng, trong khi mã mà nó thay thế thì không, như bernie đã nhận xét trong câu trả lời của mình .

Nếu đây là trường hợp, thì bạn có thể xử lý nó bằng cách sử dụng unique_ptrthay vì vectorhoặc, nếu trường hợp đó không phải là ngoại lệ trong dòng mã của bạn, hãy thực sự viết một lớp buffer_ownersẽ sở hữu bộ nhớ đó và cho phép bạn truy cập dễ dàng và an toàn vào nó, bao gồm tiền thưởng như thay đổi kích thước nó (sử dụng realloc?), hoặc bất cứ thứ gì bạn cần.


1
Cảm ơn vì đã giải quyết các mảng tĩnh - std :: vector là vô ích nếu bạn không được phép phân bổ động bộ nhớ vì lý do hiệu suất.
Tom

10
Khi bạn nói "Sử dụng một mảng tĩnh sẽ có tốc độ nhanh nhất như phiên bản boost :: mảng", nó cho thấy mức độ thiên vị của bạn. Nó phải là cái khác xung quanh, Boost: mảng có thể nhanh nhất như mảng tĩnh.
toto

3
@toto: Đó là một sự hiểu lầm: Bạn nên đọc nó là "Sử dụng một mảng tĩnh sẽ là tốt nhất ((nhanh như phiên bản boost :: mảng) && (ít an toàn hơn nhiều))". Tôi sẽ chỉnh sửa bài viết để làm rõ điều này. Nhân tiện, cảm ơn bạn vì lợi ích của sự nghi ngờ.
paercebal

1
Còn std :: mảng thì sao?
paulm

4
Luôn luôn hiển thị báo giá đầy đủ. "Các lập trình viên lãng phí rất nhiều thời gian để suy nghĩ hoặc lo lắng về tốc độ của các phần không văn bản trong chương trình của họ và những nỗ lực này có hiệu quả thực sự có tác động tiêu cực mạnh khi gỡ lỗi và bảo trì. Chúng ta nên quên đi những hiệu quả nhỏ, nói về 97% thời gian: tối ưu hóa sớm là gốc rễ của mọi tội lỗi. Tuy nhiên, chúng ta không nên bỏ qua cơ hội của mình trong 3% quan trọng đó. " Nếu không, nó trở thành một soundbite vô nghĩa.
biến thái

32

Các vectơ là các mảng dưới mui xe. Hiệu suất là như nhau.

Một nơi mà bạn có thể gặp phải vấn đề về hiệu năng, không bắt đầu kích thước chính xác vectơ.

Khi một vectơ lấp đầy, nó sẽ tự thay đổi kích thước và điều đó có thể ngụ ý, một phân bổ mảng mới, theo sau là n constructor sao chép, theo sau là khoảng n lệnh gọi hàm, sau đó là xóa mảng.

Nếu cấu trúc / phá hủy của bạn là đắt tiền, tốt hơn hết là bạn nên tạo vectơ có kích thước chính xác để bắt đầu.

Có một cách đơn giản để chứng minh điều này. Tạo một lớp đơn giản hiển thị khi nó được xây dựng / hủy / sao chép / gán. Tạo một vectơ của những điều này, và bắt đầu đẩy chúng vào mặt sau của vectơ. Khi vectơ lấp đầy, sẽ có một tầng hoạt động khi vectơ thay đổi kích thước. Sau đó thử lại với vectơ có kích thước đến số phần tử dự kiến. Bạn sẽ thấy sự khác biệt.


4
Mặt dây chuyền: hiệu suất có cùng một O. std :: vector lớn làm một ít sổ sách kế toán, có lẽ tốn một ít thời gian. OTOH, cuối cùng bạn sẽ thực hiện nhiều kế toán tương tự khi cuộn các mảng động của riêng bạn.
dmckee --- ex-moderator mèo con

tôi hiểu. Sự thúc đẩy của câu hỏi của anh ấy là sự khác biệt về hiệu suất ..... Tôi đã cố gắng giải quyết điều đó.
EvilTeach

Stcc :: vector của Gcc thực sự tăng công suất từng cái một nếu bạn gọi Push_back.
bjhend

3
@bjhend Sau đó, std::vectorâm thanh của gcc không tuân thủ? Tôi tin rằng tiêu chuẩn yêu cầu rằng vector::push_backđộ phức tạp không đổi được khấu hao và tăng thêm 1 công suất cho mỗi độ push_backsẽ là n ^ 2 độ phức tạp sau khi bạn tính đến reallocs. - giả sử một số loại tăng công suất theo cấp số nhân push_backinsert, một sự thất bại reservesẽ dẫn đến nhiều nhất là sự gia tăng yếu tố không đổi trong các bản sao nội dung vector. Hệ số tăng trưởng vectơ 1,5 theo cấp số nhân có nghĩa là ~ gấp 3 lần số lượng bản sao nếu bạn không thành công reserve().
Yakk - Adam Nevraumont

3
@bjhend bạn sai rồi. Tiêu chuẩn cấm tăng trưởng theo cấp số nhân: § 23.2.3 đoạn 16 cho biết "Bảng 101 liệt kê các hoạt động được cung cấp cho một số loại container theo trình tự nhưng không phải là các hoạt động khác. sẽ thực hiện chúng để mất thời gian cố định. " (bảng 101 là bảng có Push_back trong đó). Bây giờ xin vui lòng ngừng lan truyền FUD. Không thực hiện chính thống vi phạm yêu cầu này. Thư viện C ++ tiêu chuẩn của Microsoft phát triển với hệ số 1,5 lần và GCC phát triển với hệ số 2x.
R. Martinho Fernandes

27

Để đáp ứng với một cái gì đó Mehrdad nói:

Tuy nhiên, có thể có trường hợp bạn vẫn cần mảng. Khi giao tiếp với mã cấp thấp (nghĩa là lắp ráp) hoặc các thư viện cũ yêu cầu mảng, bạn có thể không sử dụng được vectơ.

Không đúng chút nào. Các vectơ xuống cấp độc đáo thành mảng / con trỏ nếu bạn sử dụng:

vector<double> vector;
vector.push_back(42);

double *array = &(*vector.begin());

// pass the array to whatever low-level code you have

Điều này làm việc cho tất cả các triển khai STL chính. Trong tiêu chuẩn tiếp theo, nó sẽ được yêu cầu để làm việc (mặc dù ngày nay nó chỉ hoạt động tốt).


1
Các tiêu chuẩn hiện tại nói không có điều đó. Nó được ngụ ý, và nó được thực hiện như là lưu trữ liên tục. Nhưng tiêu chuẩn chỉ nói rằng nó là một thùng chứa truy cập ngẫu nhiên (sử dụng các trình vòng lặp). Các tiêu chuẩn tiếp theo sẽ rõ ràng.
Frank Krueger

1
& * v.begin () chỉ áp dụng toán tử & cho kết quả của việc hủy tham chiếu trình lặp. Không tham chiếu có thể trả về bất kỳ loại nào. Sử dụng địa chỉ của toán tử có thể trả về bất kỳ loại nào. Tiêu chuẩn không định nghĩa đây là một con trỏ vào vùng nhớ liền kề.
Frank Krueger

15
Văn bản gốc của Tiêu chuẩn năm 1998 thực sự không yêu cầu nó, nhưng đã có một phụ lục vào năm 2003 giải quyết vấn đề này, vì vậy nó thực sự được bảo vệ bởi Tiêu chuẩn. Herbutter.wordpress.com/2008/04/07/ từ
Nemanja Trifunovic

2
C ++ 03 nói rõ ràng &v[n] == &v[0] + nlà hợp lệ được cung cấp nnằm trong phạm vi kích thước. Đoạn chứa tuyên bố này đã không thay đổi với C ++ 11.
bjhend

2
Tại sao không chỉ sử dụng std :: vector :: data ()?
paulm

15

Bạn thậm chí còn có ít lý do hơn để sử dụng mảng đơn giản trong C ++ 11.

Có 3 loại mảng trong tự nhiên từ nhanh nhất đến chậm nhất, tùy thuộc vào các tính năng mà chúng có (tất nhiên chất lượng thực hiện có thể khiến mọi thứ thực sự nhanh chóng ngay cả đối với trường hợp 3 trong danh sách):

  1. Tĩnh với kích thước được biết tại thời gian biên dịch. ---std::array<T, N>
  2. Năng động với kích thước được biết đến trong thời gian chạy và không bao giờ thay đổi kích thước. Tối ưu hóa điển hình ở đây là, nếu mảng có thể được phân bổ trực tiếp trong ngăn xếp. - Không có sẵn . Có thể dynarraytrong C ++ TS sau C ++ 14. Trong C có VLAs
  3. Năng động và thay đổi kích thước khi chạy. ---std::vector<T>

Đối với 1. mảng tĩnh đơn giản với số phần tử cố định, sử dụng std::array<T, N>trong C ++ 11.

Cho 2. mảng kích thước cố định được chỉ định trong thời gian chạy, nhưng điều đó sẽ không thay đổi kích thước của chúng, có thảo luận trong C ++ 14 nhưng cuối cùng nó đã được chuyển sang đặc tả kỹ thuật và được tạo ra từ C ++ 14.

Cho 3. std::vector<T> thường sẽ yêu cầu bộ nhớ trong heap . Điều này có thể có hậu quả về hiệu suất, mặc dù bạn có thể sử dụng std::vector<T, MyAlloc<T>>để cải thiện tình hình với bộ cấp phát tùy chỉnh. Ưu điểm so với T mytype[] = new MyType[n];là bạn có thể thay đổi kích thước của nó và nó sẽ không phân rã thành một con trỏ, như các mảng đơn giản làm.

Sử dụng các loại thư viện tiêu chuẩn được đề cập để tránh mảng phân rã thành con trỏ . Bạn sẽ tiết kiệm thời gian gỡ lỗi và hiệu suất hoàn toàn giống với mảng đơn giản nếu bạn sử dụng cùng một bộ tính năng.


2
std :: dynarray. Sau khi xem xét các bình luận của cơ quan quốc gia đến n3690, thành phần thư viện này đã được bỏ phiếu từ tài liệu làm việc C ++ 14 thành một Đặc tả kỹ thuật riêng biệt. Container này không phải là một phần của dự thảo C ++ 14 kể từ n3797. từ en.cppreference.com/w/cpp/container/dynarray
Mohamed El-Nakib

1
câu trả lời rất tốt ngắn gọn và tóm tắt, nhưng nhiều chi tiết hơn bất kỳ.
Mohamed El-Nakib

6

Đi với STL. Không có hình phạt hiệu suất. Các thuật toán rất hiệu quả và chúng làm rất tốt việc xử lý các loại chi tiết mà hầu hết chúng ta không nghĩ tới.


5

STL là một thư viện được tối ưu hóa mạnh mẽ. Trên thực tế, thậm chí còn đề xuất sử dụng STL trong các trò chơi có thể cần hiệu suất cao. Mảng quá dễ bị lỗi trong các nhiệm vụ hàng ngày. Trình biên dịch ngày nay cũng rất thông minh và thực sự có thể tạo ra mã tuyệt vời với STL. Nếu bạn biết những gì bạn đang làm, STL thường có thể cung cấp hiệu suất cần thiết. Ví dụ: bằng cách khởi tạo vectơ đến kích thước yêu cầu (nếu bạn biết từ đầu), về cơ bản bạn có thể đạt được hiệu suất mảng. Tuy nhiên, có thể có trường hợp bạn vẫn cần mảng. Khi giao tiếp với mã cấp thấp (nghĩa là lắp ráp) hoặc các thư viện cũ yêu cầu mảng, bạn có thể không sử dụng được vectơ.


4
do vectơ đó liền kề nhau, giao diện với các thư viện yêu cầu mảng vẫn khá dễ dàng.
Greg Rogers

Có, nhưng nếu bạn muốn gây rối với nội dung của vectơ, sẽ có ít lợi thế hơn trong việc sử dụng vectơ. Nhân tiện, từ khóa là "có thể không."
Mehrdad Afshari

3
chỉ có một trường hợp tôi biết về nơi các vectơ không thể được sử dụng: nếu kích thước bằng 0. thì & a [0] hoặc & * a.begin () sẽ không hoạt động. c ++ 1x sẽ khắc phục điều đó bằng cách giới thiệu hàm a.data () trả về bộ đệm bên trong giữ các phần tử
Johannes Schaub - litb

Kịch bản cụ thể trong tâm trí của tôi khi tôi viết đó là mảng dựa trên ngăn xếp.
Mehrdad Afshari

1
Vectơ xen kẽ hoặc bất kỳ vùng chứa liền kề nào có C: vec.data()cho dữ liệu và vec.size()kích thước. Nó là dễ dàng.
Germán Diago

5

Về sự đóng góp của duli .

Kết luận là mảng số nguyên nhanh hơn vectơ số nguyên (5 lần trong ví dụ của tôi). Tuy nhiên, các mảng và vectơ có cùng tốc độ cho dữ liệu phức tạp hơn / không được căn chỉnh.


3

Nếu bạn biên dịch phần mềm ở chế độ gỡ lỗi, nhiều trình biên dịch sẽ không nội tuyến các hàm truy cập của vectơ. Điều này sẽ làm cho việc thực hiện vector stl chậm hơn nhiều trong trường hợp hiệu suất là một vấn đề. Nó cũng sẽ làm cho mã dễ gỡ lỗi hơn vì bạn có thể thấy trong trình gỡ lỗi có bao nhiêu bộ nhớ được phân bổ.

Trong chế độ tối ưu hóa, tôi mong muốn vector stl tiếp cận hiệu quả của một mảng. Điều này là do nhiều phương thức vectơ hiện được nội tuyến.


Điều này rất quan trọng để đề cập. Hồ sơ gỡ lỗi công cụ STL là rất, rất chậm. Và đó là một trong những lý do tại sao mọi người điều STL chậm.
Erik Aronesty

3

Chắc chắn có một tác động hiệu suất để sử dụng std::vectorso với một mảng thô khi bạn muốn một bộ đệm chưa được khởi tạo (ví dụ: sử dụng làm đích cho memcpy()). An std::vectorsẽ khởi tạo tất cả các phần tử của nó bằng cách sử dụng hàm tạo mặc định. Một mảng thô sẽ không.

Thông số c ++ cho hàm std:vectortạo lấy một countđối số (đó là dạng thứ ba) nêu:

`Xây dựng một bộ chứa mới từ nhiều nguồn dữ liệu khác nhau, tùy ý sử dụng phân bổ cấp phát do người dùng cung cấp.

3) Xây dựng vùng chứa với các phiên bản được chèn mặc định của T. Không có bản sao nào được tạo.

Phức tạp

2-3) Tính tuyến tính

Một mảng thô không phát sinh chi phí khởi tạo này.

Xem thêm Làm thế nào tôi có thể tránh std :: vector <> để khởi tạo tất cả các phần tử của nó?


2

Sự khác biệt về hiệu năng giữa hai loại phụ thuộc rất nhiều vào việc triển khai - nếu bạn so sánh một std :: vector được triển khai kém với một triển khai mảng tối ưu, mảng sẽ thắng, nhưng xoay lại và vector sẽ thắng ...

Miễn là bạn so sánh táo với táo (cả mảng và vectơ đều có số phần tử cố định hoặc cả hai được thay đổi kích thước một cách linh hoạt) Tôi sẽ nghĩ rằng sự khác biệt hiệu suất là không đáng kể miễn là bạn tuân theo thực hành mã hóa STL. Đừng quên rằng việc sử dụng các bộ chứa C ++ tiêu chuẩn cũng cho phép bạn sử dụng các thuật toán được cuộn sẵn là một phần của thư viện C ++ tiêu chuẩn và hầu hết chúng có khả năng hoạt động tốt hơn so với việc thực hiện trung bình cùng một thuật toán mà bạn tự xây dựng .

Điều đó nói rằng, IMHO vectơ thắng trong kịch bản gỡ lỗi với STL gỡ lỗi vì hầu hết các triển khai STL với chế độ gỡ lỗi thích hợp ít nhất có thể làm nổi bật / cathc các lỗi điển hình của mọi người khi làm việc với các thùng chứa tiêu chuẩn.

Ồ, và đừng quên rằng mảng và vectơ có chung bố cục bộ nhớ để bạn có thể sử dụng vectơ để truyền dữ liệu tới mã C hoặc C ++ kế thừa, mong đợi các mảng cơ bản. Tuy nhiên, hãy nhớ rằng hầu hết các cược đã tắt trong kịch bản đó và bạn sẽ xử lý lại bộ nhớ thô.


1
Tôi nghĩ rằng để đáp ứng các yêu cầu về hiệu suất (O (1) tra cứu và chèn), bạn gần như phải thực hiện std :: vector <> bằng cách sử dụng mảng động. Chắc chắn đây là cách rõ ràng để làm điều đó.
dmckee --- ex-moderator mèo con

Không chỉ là các yêu cầu về hiệu suất, mà còn là yêu cầu lưu trữ liên tục. Việc triển khai vectơ xấu sẽ đặt quá nhiều lớp không xác định giữa mảng và API. Việc triển khai vectơ tốt sẽ cho phép mã nội tuyến, SIMD được sử dụng trên các vòng lặp, v.v.
Max Lybbert

Việc thực hiện véc tơ xấu như được mô tả sẽ không tuân thủ tiêu chuẩn. Nếu bạn muốn gián tiếp, std::dequecó thể được sử dụng.
Phil1970

1

Nếu bạn không cần phải điều chỉnh kích thước một cách linh hoạt, bạn có chi phí bộ nhớ để tiết kiệm dung lượng (một con trỏ / size_t). Đó là nó.


1

Có thể có một số trường hợp cạnh mà bạn có quyền truy cập vectơ bên trong một hàm nội tuyến bên trong một hàm nội tuyến, trong đó bạn đã vượt ra ngoài những gì trình biên dịch sẽ nội tuyến và nó sẽ buộc một lệnh gọi hàm. Điều đó sẽ hiếm đến mức không đáng lo ngại - nói chung tôi sẽ đồng ý với litb .

Tôi ngạc nhiên chưa ai đề cập đến điều này - đừng lo lắng về hiệu suất cho đến khi nó được chứng minh là có vấn đề, sau đó là điểm chuẩn.


1

Tôi cho rằng mối quan tâm chính không phải là hiệu suất, mà là an toàn. Bạn có thể mắc rất nhiều lỗi với mảng (ví dụ như xem xét thay đổi kích thước), trong đó một vectơ sẽ giúp bạn tiết kiệm rất nhiều nỗi đau.


1

Các vectơ sử dụng bộ nhớ nhiều hơn một chút so với mảng vì chúng chứa kích thước của mảng. Chúng cũng làm tăng kích thước đĩa cứng của các chương trình và có thể là dung lượng bộ nhớ của các chương trình. Những sự gia tăng này rất nhỏ, nhưng có thể quan trọng nếu bạn đang làm việc với một hệ thống nhúng. Mặc dù hầu hết những nơi mà những khác biệt này là những nơi bạn sẽ sử dụng C thay vì C ++.


2
Nếu điều này quan trọng, thì rõ ràng bạn không sử dụng các mảng có kích thước động và do đó, các mảng của bạn không cần thay đổi kích thước. (Nếu họ đã làm, bạn sẽ lưu trữ kích thước bằng cách nào đó). Do đó, bạn cũng có thể sử dụng boost :: mảng trừ khi tôi nhầm - và điều gì khiến bạn nói rằng cần phải "lưu trữ kích thước" ở đâu đó?
Arafangion

1

Bài kiểm tra đơn giản sau:

Giải thích kiểm tra hiệu năng C ++ Array vs Vector

mâu thuẫn với kết luận từ "So sánh mã lắp ráp được tạo cho lập chỉ mục cơ bản, hội thảo và các hoạt động gia tăng trên các vectơ và mảng / con trỏ."

Phải có sự khác biệt giữa các mảng và vectơ. Bài kiểm tra nói vậy ... cứ thử đi, mã ở đó ...


1

Đôi khi mảng thực sự tốt hơn vectơ. Nếu bạn luôn thao tác một tập hợp các đối tượng có chiều dài cố định, mảng sẽ tốt hơn. Hãy xem xét các đoạn mã sau:

int main() {
int v[3];
v[0]=1; v[1]=2;v[2]=3;
int sum;
int starttime=time(NULL);
cout << starttime << endl;
for (int i=0;i<50000;i++)
for (int j=0;j<10000;j++) {
X x(v);
sum+=x.first();
}
int endtime=time(NULL);
cout << endtime << endl;
cout << endtime - starttime << endl;

}

trong đó phiên bản vectơ của X là

class X {
vector<int> vec;
public:
X(const vector<int>& v) {vec = v;}
int first() { return vec[0];}
};

và phiên bản mảng của X là:

class X {
int f[3];

public:
X(int a[]) {f[0]=a[0]; f[1]=a[1];f[2]=a[2];}
int first() { return f[0];}
};

Phiên bản mảng của main () sẽ nhanh hơn vì chúng ta đang tránh chi phí "mới" mọi lúc trong vòng lặp bên trong.

(Mã này đã được đăng lên comp.lang.c ++ bởi tôi).


1

Nếu bạn đang sử dụng vectơ để thể hiện hành vi đa chiều, có một điểm nhấn về hiệu suất.

Các vectơ 2d + có gây ra hiệu suất không?

Điểm chính là có một lượng nhỏ chi phí với mỗi vectơ con có thông tin kích thước và không nhất thiết phải có tuần tự hóa dữ liệu (như có các mảng c đa chiều). Việc thiếu tuần tự hóa này có thể cung cấp lớn hơn các cơ hội tối ưu hóa vi mô. Nếu bạn đang thực hiện các mảng đa chiều, tốt nhất chỉ nên mở rộng std :: vector và cuộn chức năng get / set / resize bit của riêng bạn.


0

Giả sử một mảng có độ dài cố định (ví dụ int* v = new int[1000];so với std::vector<int> v(1000);, với kích thước vđược giữ cố định ở 1000), việc xem xét hiệu suất duy nhất thực sự quan trọng (hoặc ít nhất là quan trọng với tôi khi tôi rơi vào tình huống khó xử tương tự) là tốc độ truy cập vào một thành phần. Tôi đã tra cứu mã vector của STL và đây là những gì tôi tìm thấy:

const_reference
operator[](size_type __n) const
{ return *(this->_M_impl._M_start + __n); }

Hàm này chắc chắn sẽ được trình biên dịch nội tuyến. Vì vậy, miễn là điều duy nhất bạn dự định làm vlà truy cập các yếu tố của nó operator[], có vẻ như không thực sự có bất kỳ sự khác biệt nào về hiệu suất.

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.