2009-08-11 31 views
65

Container STL nào phù hợp với nhu cầu của tôi nhất? Về cơ bản tôi có một thùng chứa 10 phần tử rộng, trong đó tôi liên tục push_back các phần tử mới trong khi pop_front nhập phần tử cũ nhất (khoảng một triệu lần).Tôi nên sử dụng container STL nào cho FIFO?

Tôi hiện đang sử dụng một std::deque cho công việc nhưng đã tự hỏi nếu một std::list sẽ hiệu quả hơn vì tôi sẽ không cần phải phân bổ lại bản thân (hoặc có lẽ tôi đang nhầm một std::deque cho một std::vector?). Hoặc là có một container hiệu quả hơn cho nhu cầu của tôi?

P.S. Tôi không cần truy cập ngẫu nhiên

+5

Tại sao không dùng thử cả thời gian để xem cái nào nhanh hơn cho nhu cầu của bạn? – KTC

+2

Tôi sắp sửa làm điều này, nhưng tôi cũng đang tìm kiếm một câu trả lời lý thuyết. –

+1

'std :: deque' sẽ không phân bổ lại. Nó là một kết hợp của một 'std :: list' và' std :: vector' trong đó nó phân bổ các khối lớn hơn một 'std :: list' nhưng sẽ không tái phân bổ như' std :: vector'. –

Trả lời

151

Vì có vô số các câu trả lời, bạn có thể bị nhầm lẫn, nhưng để tóm tắt:

Sử dụng một std::queue. Lý do cho điều này là đơn giản: nó là một cấu trúc FIFO. Bạn muốn FIFO, bạn sử dụng một std::queue.

Điều này làm cho ý định của bạn rõ ràng với bất kỳ ai khác và thậm chí cả chính bạn. Một std::list hoặc std::deque thì không. Một danh sách có thể chèn và loại bỏ bất cứ nơi nào, mà không phải là những gì một cấu trúc FIFO là giả sử để làm, và một deque có thể thêm và loại bỏ từ hai đầu, mà cũng là một cái gì đó một cấu trúc FIFO không thể làm.

Đây là lý do bạn nên sử dụng queue.

Bây giờ, bạn đã hỏi về hiệu suất. Trước tiên, hãy luôn nhớ quy tắc quan trọng này: Đầu tiên là mã tốt, hiệu suất cuối cùng.

Lý do cho việc này rất đơn giản: những người cố gắng thực hiện trước khi làm sạch và sang trọng hầu như luôn luôn kết thúc cuối cùng. Mã của họ trở thành một mảnh của mush, bởi vì họ đã từ bỏ tất cả những gì là tốt để thực sự không có gì ra khỏi nó.

Bằng cách viết mã tốt, dễ đọc trước, hầu hết các vấn đề về hiệu năng sẽ tự giải quyết. Và nếu sau này bạn thấy hiệu suất của mình thiếu, giờ đây bạn có thể dễ dàng thêm một trình lược tả vào mã đẹp, sạch sẽ của bạn và tìm ra vấn đề ở đâu.

Điều đó tất cả đã nói, std::queue chỉ là bộ điều hợp. Nó cung cấp giao diện an toàn, nhưng sử dụng một container khác ở bên trong. Bạn có thể chọn vùng chứa cơ bản này và điều này cho phép một sự linh hoạt tốt.

Vì vậy, bạn nên sử dụng vùng chứa cơ bản nào? Chúng tôi biết rằng std::liststd::deque đều cung cấp các chức năng cần thiết (push_back(), pop_front()front()), vì vậy, làm cách nào để chúng tôi quyết định?

Trước tiên, hãy hiểu rằng việc cấp phát bộ nhớ (và deallocating) không phải là một việc nhanh chóng để làm, nói chung, bởi vì nó liên quan đến việc đi ra ngoài hệ điều hành và yêu cầu nó làm điều gì đó. Một list phải phân bổ bộ nhớ mỗi lần một cái gì đó được thêm vào, và deallocate nó khi nó biến mất. Mặt khác,

Mặt khác, phân bổ theo khối. Nó sẽ phân bổ ít thường xuyên hơn list. Hãy coi nó như một danh sách, nhưng mỗi đoạn bộ nhớ có thể chứa nhiều nút. (Tất nhiên, tôi muốn đề nghị bạn really learn how it works.)

Vì vậy, với điều đó một mình deque sẽ hoạt động tốt hơn, vì nó không xử lý bộ nhớ thường xuyên. Trộn lẫn với thực tế bạn đang xử lý dữ liệu có kích thước không đổi, nó có thể sẽ không phải phân bổ sau khi vượt qua đầu tiên thông qua dữ liệu, trong khi một danh sách sẽ liên tục phân bổ và deallocating.

Điều thứ hai cần hiểu là cache performance. Đi ra ngoài RAM chậm, vì vậy khi CPU thực sự cần, nó làm cho tốt nhất trong thời gian này bằng cách lấy một đoạn của bộ nhớ trở lại với nó, vào bộ nhớ cache. Bởi vì deque phân bổ trong các khối bộ nhớ, có khả năng là việc truy cập vào một phần tử trong vùng chứa này sẽ khiến CPU mang lại phần còn lại của vùng chứa. Giờ đây, mọi truy cập thêm vào deque sẽ nhanh chóng, vì dữ liệu nằm trong bộ nhớ cache.

Điều này không giống như danh sách, trong đó dữ liệu được phân bổ một lần. Điều này có nghĩa rằng dữ liệu có thể được trải rộng khắp nơi trong bộ nhớ, và hiệu suất bộ nhớ cache sẽ là xấu.

Vì vậy, hãy xem xét rằng, một deque phải là lựa chọn tốt hơn. Đây là lý do tại sao nó là container mặc định khi sử dụng queue. Điều đó tất cả đã nói, đây vẫn chỉ là một (rất) giáo dục đoán: bạn sẽ phải cấu hình mã này, bằng cách sử dụng một deque trong một thử nghiệm và list trong khác để thực sự biết chắc chắn.

Nhưng hãy nhớ: lấy mã làm việc với giao diện sạch, sau đó lo lắng về hiệu suất.

John nêu lên mối quan tâm bao gồm list hoặc deque sẽ làm giảm hiệu suất. Một lần nữa, anh ta và tôi cũng không thể nói chắc chắn mà không tự mình lược tả nó, nhưng rất có thể là trình biên dịch sẽ nội tuyến các cuộc gọi mà queue tạo ra. Tức là, khi bạn nói queue.push(), nó sẽ thực sự chỉ nói queue.container.push_back(), bỏ qua cuộc gọi hàm hoàn toàn.

Một lần nữa, đây chỉ là một phỏng đoán được giáo dục, nhưng việc sử dụng queue sẽ không làm suy giảm hiệu suất, khi so sánh với việc sử dụng vùng chứa bên dưới. Như tôi đã nói trước đây, sử dụng queue, vì nó sạch sẽ, dễ sử dụng và an toàn, và nếu nó thực sự trở thành một hồ sơ vấn đề và thử nghiệm.

+8

+1 - và nếu nó chỉ ra rằng tăng :: circular_buffer <> có hiệu suất tốt nhất, sau đó chỉ sử dụng nó như là container bên dưới (nó cũng cung cấp push_back(), pop_front(), front() và back (bắt buộc))). –

+3

+1 được giải thích rõ ràng –

+2

Được chấp nhận để giải thích chi tiết (đó là những gì tôi cần, cảm ơn vì đã dành thời gian). Đối với hiệu suất đầu tiên của mã tốt nhất, tôi phải thừa nhận đó là một trong những mặc định lớn nhất của tôi, tôi luôn cố gắng làm điều hoàn hảo trong lần chạy đầu tiên ... Tôi đã viết mã bằng cách sử dụng một deque đầu tiên khó khăn, nhưng kể từ khi điều đó là không ' t thực hiện cũng như tôi nghĩ (nó được cho là gần như thời gian thực), tôi đoán tôi nên cải thiện nó một chút. Như Neil cũng đã nói, tôi thực sự nên đã sử dụng một profiler mặc dù ... Mặc dù tôi hạnh phúc để có những sai lầm này ngay bây giờ trong khi nó không thực sự quan trọng. Cảm ơn tất cả các bạn. –

3

A queue có lẽ là giao diện đơn giản hơn deque nhưng đối với danh sách nhỏ như vậy, sự khác biệt về hiệu suất có thể không đáng kể.

Tương tự với số list. Nó chỉ là một sự lựa chọn của những gì API bạn muốn.

+0

Nhưng tôi đã tự hỏi nếu push_back liên tục đã làm cho hàng đợi hoặc deque reallocate mình –

+0

std :: queue là một wrapper xung quanh container khác, do đó, một hàng đợi gói deque sẽ kém hiệu quả hơn một deque thô. –

+1

Đối với 10 hạng mục, hiệu suất rất có thể sẽ là một vấn đề nhỏ như vậy, rằng "hiệu quả" có thể được đo lường tốt hơn trong thời gian lập trình hơn là mã thời gian. Và các cuộc gọi từ hàng đợi đến deque bởi bất kỳ tối ưu hóa trình biên dịch phong nha sẽ được xuống không có gì. – lavinio

26

Kiểm tra std :: queue. Nó kết thúc tốt đẹp một loại container cơ bản, và container mặc định là std :: deque.

+0

Tôi nghi ngờ gói một deque với một hàng đợi sẽ có đặc tính hiệu suất tốt hơn so với chỉ sử dụng một deque. –

+0

Bạn có nghĩ rằng nó sẽ tồi tệ hơn không? Vấn đề là một hàng đợi là cấu trúc FIFO để sử dụng trong C++. Nó cung cấp giao diện chính xác và cho phép bạn chọn vùng chứa nào nó thích hợp, vì vậy nếu danh sách kết thúc tốt hơn, nó đơn giản như nói "sử dụng danh sách" và không có thay đổi mã nào khác. [http://www.cplusplus.com/reference/stl/queue/] – GManNickG

+0

Điểm tôi muốn nói với câu hỏi đầu tiên là: bạn không biết. Bạn có thể đoán, nhưng không chắc bạn sẽ thấy bất kỳ sự khác biệt hiệu suất nào. Nội tuyến sẽ loại bỏ "shell", do đó, nó giống như * sử dụng 'danh sách',' deque' hoặc bất kỳ điều gì, hiệu suất khôn ngoan, nhưng thực tế là "shell" đảm bảo bạn sử dụng cấu trúc đúng cách. – GManNickG

5

Tại sao không std::queue? Tất cả nó có là push_backpop_front.

6

tôi liên tục push_back yếu tố mới khi pop_front ing yếu tố lâu đời nhất (khoảng một triệu lần).

Một triệu thực sự không phải là một số lớn trong tính toán. Như những người khác đã đề xuất, hãy sử dụng một giải pháp đầu tiên là std::queue. Trong trường hợp không chắc của nó là quá chậm, xác định các nút cổ chai bằng cách sử dụng một hồ sơ (không đoán!) Và tái triển khai bằng cách sử dụng một container khác nhau với cùng một giao diện.

+1

Điều này là, một số lượng lớn như những gì tôi muốn làm là nghĩa vụ phải là thời gian thực Mặc dù bạn là đúng mà tôi nên đã sử dụng một hồ sơ để xác định nguyên nhân ... –

+0

Điều này, tôi không thực sự được sử dụng để sử dụng một profiler (chúng tôi đã sử dụng gprof một chút trong một trong các lớp học của chúng tôi nhưng chúng tôi thực sự không đi sâu ...). có thể chỉ cho tôi một số nguồn tài nguyên, tôi sẽ đánh giá rất cao! PS. Tôi sử dụng VS2008 –

+0

@Gab: VS2008 nào bạn có (Express, Pro ...)? Một số đi kèm với một hồ sơ. – sbi

Các vấn đề liên quan