Về cơ bản có 3 nguồn chi phí liên quan đến việc liên tục phụ thêm yếu tố để một container động:
- Quản lý bộ nhớ.
- Sổ kế toán nội bộ của vùng chứa.
- Bất kỳ hoạt động nào cần phải được thực hiện trên chính các phần tử đó. Đáng chú ý; bất kỳ vùng chứa nào làm mất hiệu lực tham chiếu khi chèn đều có khả năng di chuyển/sao chép các phần tử xung quanh.
Hãy bắt đầu với 1. vector
giữ yêu cầu gấp đôi so với bộ nhớ, và deque
phân bổ khối có kích thước cố định (deque
thường thực hiện như một mảng của mảng, với các mảng tầng thấp hơn là kích thước cố định). Yêu cầu bộ nhớ nhiều hơn có thể mất nhiều thời gian hơn yêu cầu ít hơn, nhưng thông thường trừ khi đống của bạn là rất phân mảnh yêu cầu cho một đoạn lớn cùng một lúc là cách nhanh nhất để có được một số bộ nhớ. Nó có thể nhanh hơn để phân bổ một meg một lần, sau đó yêu cầu một kilobyte 1000 lần. Vì vậy, có vẻ như rõ ràng rằng vector
cuối cùng sẽ có lợi thế ở đây (cho đến khi container quá lớn nó bị ảnh hưởng bởi sự phân mảnh). Tuy nhiên, điều này không phải là cuối cùng: bạn chỉ yêu cầu 1000 yếu tố. Tôi đã viết mã sau đây http://coliru.stacked-crooked.com/a/418b18ff8a81c1c0. Nó không phải là rất thú vị nhưng về cơ bản nó sử dụng một phân bổ tầm thường mà tăng một toàn cầu để xem có bao nhiêu phân bổ được thực hiện.
Trong quá trình chuẩn mực, vector
yêu cầu bộ nhớ 11 lần và deque
chỉ 10. deque
tiếp tục yêu cầu số tiền tương tự, vector
yêu cầu số tiền gấp đôi. Đồng thời, vector
phải gọi số free
10 lần. Và deque
0. Điều này có vẻ giống như một chiến thắng nhỏ cho deque
.
Đối với sổ sách kế toán nội bộ, vector
có triển khai đơn giản hơn deque
. Xét cho cùng, vector
chỉ là một mảng động và deque
là một mảng các mảng và hoàn toàn phức tạp hơn. Vì vậy, đây rõ ràng là một chiến thắng cho vector
.
Cuối cùng, các yếu tố tự vận hành. Trong deque
, không có gì được di chuyển. Với vector
, mọi phân bổ đống mới cũng liên quan đến việc di chuyển tất cả các phần tử. Nó có thể được tối ưu hóa để sử dụng memcpy
cho các loại tầm thường, nhưng thậm chí thấy, đó là 10 cuộc gọi đến memcpy
để sao chép 1, 2, 4, 8 ... 512 số nguyên. Đây rõ ràng là một chiến thắng cho deque
.
Tôi có thể suy đoán rằng cranking lên đến O3
cho phép nội suy rất tích cực của nhiều mã phức tạp hơn trong deque
, giảm trọng số 2. Nhưng rõ ràng, trừ khi bạn làm một chi tiết hơn (rất cẩn thận!) Điểm chuẩn, bạn sẽ không bao giờ biết chắc chắn.
Chủ yếu, bài đăng này là để cho thấy rằng nó phức tạp hơn so với chỉ đơn giản là một container chuyên ngành so với một tổng quát hơn. Tôi sẽ đưa ra một dự đoán mặc dù (đặt cổ của tôi ra để được cắt bỏ, như nó đã được): nếu bạn tăng số lượng các yếu tố bởi thậm chí nói một yếu tố của 2 hoặc 4, bạn sẽ không nhìn thấy deque
giành chiến thắng nữa. Đó là bởi vì deque
sẽ tạo ra gấp đôi hoặc gấp đôi số lần phân bổ heap, nhưng véc tơ sẽ chỉ tạo thêm 1-2 lần nữa.
Tôi cũng có thể lưu ý ở đây rằng deque
thực sự là một cấu trúc dữ liệu lẻ; về mặt lý thuyết là một mảng các mảng nhưng trong nhiều triển khai mảng đó là một kích thước nhất định, hoặc chỉ một phần tử, tùy theo cái nào lớn hơn. Ngoài ra, một số bảo đảm O lớn là vô nghĩa. push_back
chỉ cố định thời gian cố định, bởi vì trong C++, chỉ hoạt động trên các phần tử được tính vào O lớn. Nếu không, nó phải rõ ràng, vì nó là mảng mảng, mảng cấp cao nhất sẽ có tỷ lệ thuận với số lượng các phần tử đã được lưu trữ. Và cuối cùng là mảng cấp cao nhất chạy ra khỏi phòng, và bạn phải tái phân bổ nó, di chuyển con trỏ O (N). Vì vậy, nó không thực sự O (1) push_back
.
Tôi đã đảm bảo đủ dung lượng cho véc tơ, véc tơ sẽ nhanh hơn. –
@RHertel Nếu bạn sử dụng đồng bằng 'std :: list', tôi mong đợi chèn là * chậm hơn *, bởi vì mỗi lần chèn sẽ phân bổ một nút mới. – dyp
@RHertel danh sách có vẻ chậm hơn; không gây sốc, đó là lý do tại sao tôi không đề cập đến nó. –