2008-10-19 24 views
6

Trong lập trình chúng tôi phải đối mặt với các tình huống khác nhau, nơi chúng tôi được yêu cầu phải sử dụng STL container trung gian là các ví dụ sau đây mô tả:C++ STL: Container Giải trí hoặc Tái sử dụng sau khi thanh toán bù trừ?

while(true) 
{ 
    set <int> tempSet; 

    for (int i = 0; i < n; i ++) 
    { 
     if (m.size() == min && m.size() <= max) 
     { 
      tempSet.insert(i); 
     } 
    } 
    //Some condition testing code 
} 

Hoặc

set <int> tempSet; 

while(true) 
{ 
    for (int i = 0; i < n; i ++) 
    { 
     if (m.size() == min && m.size() <= max) 
     { 
      tempSet.insert(i); 
     } 
    } 
    tempSet.clear(); 

    //Some condition testing code 
} 

Những cách tiếp cận tốt hơn về thời gian và không gian phức tạp xem xét hiện trạng của C++ compliers?

Trả lời

2

Thứ hai có thể tốt hơn một chút, nhưng sự khác biệt sẽ cực kỳ tối thiểu - mã vẫn phải trải qua và giải phóng từng mục trong bộ, bất kể nó đang tạo lại hay xóa nó.

(Không gian thông minh sẽ giống hệt nhau.)

0

Theo nguyên tắc chung, có chi phí phân bổ bộ nhớ liên quan đến việc tải dữ liệu vào vùng chứa. Tốt hơn hết là tránh phải trả chi phí đó nhiều lần, bằng cách tái sử dụng một container. Nếu bạn thực sự biết có bao nhiêu mục, hoặc có thể đoán trước, bạn nên preallocate không gian trong container lên phía trước. Nó đặc biệt đáng chú ý nếu bạn đang chèn các vật thể vào thùng chứa, bởi vì bạn có thể phải trả thêm rất nhiều chi phí xây dựng/phá hủy, vì container nhận ra rằng nó quá nhỏ, tái phân bổ bộ nhớ và sao chép xây dựng các đối tượng mới vào bộ nhớ mới dựa trên các đối tượng hiện có.

Luôn luôn preallocate và tái sử dụng. Nó giúp tiết kiệm thời gian và trí nhớ.

+0

Tôi không nghĩ rằng bạn có thể preallocate không gian trong một tập. –

+0

Vâng, tôi hiểu điều đó. Bạn có lẽ sẽ phải làm một cái gì đó ngốc nghếch với người cấp phát sau đó để có được một preallocation phong nha. – EvilTeach

+0

Tôi đã chạy thử nghiệm. max_size là rất lớn. Nó không phải là một vấn đề trong trường hợp này. – EvilTeach

4

Nếu đây là câu hỏi của set/map/list, nó sẽ không tạo ra bất kỳ sự khác biệt nào. Nếu đó là câu hỏi của vector/hash_set/hash_map/string, giây thứ hai sẽ nhanh hơn hoặc cùng tốc độ. Tất nhiên, sự khác biệt về tốc độ này sẽ chỉ đáng chú ý nếu bạn đang thực hiện một số lượng lớn các hoạt động (10.000 ints được đẩy vào một số vector 10.000 gấp khoảng hai lần nhanh - 3 giây và một số thay đổi so với 7 giây.).

Nếu bạn đang làm việc giống như lưu trữ struct/class trong cấu trúc dữ liệu thay vì con trỏ thành một, sự khác biệt này sẽ còn lớn hơn bởi vì trên mỗi thay đổi kích thước, bạn phải sao chép tất cả các phần tử.

Một lần nữa, trong mọi trường hợp, nó sẽ không thành vấn đề - và trong trường hợp nó quan trọng, nó sẽ hiển thị nếu bạn đang tối ưu hóa, và bạn quan tâm đến từng chút hiệu suất.

+0

Câu hỏi này là về tập hợp chứ không phải vectơ. Cũng cho các vectơ, tôi tranh luận về việc nhận thấy sự khác biệt về tốc độ: các vectơ được cho là tăng theo cấp số nhân (thường là gấp đôi mỗi lần), do đó số lượng phân bổ được phân bổ. –

+0

Câu hỏi không cụ thể cho một trong hai. Nếu bạn dành thời gian để thực sự chạy một số kiểm tra tốc độ cho trường hợp trong đó là vấn đề, bạn sẽ nhận thấy một hit hiệu suất lớn. Nó không phải là phân bổ mà đau, nó là memcpy sau khi realloc. – hazzen

+0

@ ChrisJester-Young: Có, số lượng phân bổ được khấu hao, nhưng phân bổ 'log (n)' tốt hơn 'Mlog (n)'. –

7

Tôi nói trường hợp đầu tiên có tính năng chống lỗi hơn. Bạn sẽ không phải nhớ để xóa nó (nó sẽ chỉ đi ra khỏi phạm vi và được thực hiện). :-)

Hiệu suất khôn ngoan không có nhiều sự khác biệt, nhưng bạn vẫn nên đo cả hai trường hợp và tự mình xem.

+1

Theo dõi nhận xét chống lỗi cho OP: Hãy tưởng tượng điều gì sẽ xảy ra khi thêm tuyên bố tiếp tục ở đâu đó trong vòng lặp while. – dalle

+0

Đúng, hoàn toàn đồng ý! :-) –

0

Có rất ít sự khác biệt, mặc dù cách thứ hai của bạn tránh gọi hàm tạo và hàm hủy nhiều lần. Mặt khác, cái đầu tiên của bạn là một dòng ngắn hơn và đảm bảo rằng vùng chứa không hiển thị bên ngoài phạm vi của vòng lặp.

15

Phiên bản đầu tiên là chính xác. Nó đơn giản hơn theo mọi cách. Dễ dàng hơn để viết, dễ đọc hơn, dễ hiểu hơn, dễ bảo trì hơn, v.v.

Phiên bản thứ hai có thể nhanh hơn, nhưng sau đó lại không thể. Bạn sẽ cần phải cho thấy rằng nó có một lợi thế đáng kể trước khi sử dụng nó.Trong hầu hết các trường hợp không tầm thường, tôi đoán rằng sẽ không có sự khác biệt về hiệu suất đo lường giữa hai trường hợp.

Đôi khi trong lập trình nhúng, rất hữu ích khi tránh đặt mọi thứ lên ngăn xếp; trong trường hợp đó, phiên bản thứ hai sẽ chính xác.

Sử dụng phiên bản đầu tiên theo mặc định; sử dụng thứ hai chỉ khi bạn có thể đưa ra một lý do chính đáng để làm như vậy (nếu lý do là hiệu suất, thì bạn nên có bằng chứng rằng lợi ích là đáng kể).

+1

Tôi đồng ý: không làm cho các biến hiển thị ở những nơi không cần thiết, trừ khi có lý do chính đáng. – xtofl

1

Sẽ có rất ít sự khác biệt về hiệu suất giữa hai thiết bị. Bạn nên đưa ra quyết định dựa trên khả năng đọc mã và độ mạnh mẽ. Tôi nghĩ ví dụ đầu tiên dễ đọc hơn và an toàn hơn - điều xảy ra trong thời gian sáu tháng khi bạn thêm điều kiện vào vòng lặp, có lẽ với tiếp tục ở đâu đó - trong ví dụ thứ hai, nó sẽ bỏ qua() và bạn sẽ có một lỗi.

1

Bạn đang đặt tập hợp của mình trên ngăn xếp, chi phí phân bổ gần bằng không!

+0

Chi phí phân bổ cho đối tượng thiết lập chính nó là gần bằng không, nhưng các mục bên trong nó vẫn được cấp phát từ heap. –

+1

@Head Geek: Việc xây dựng/phá hủy các đối tượng trong tập hợp là giống nhau trong một trong hai ví dụ - sự khác biệt duy nhất là việc xây dựng/hủy diệt đối tượng đã đặt. Vì vậy, điểm của ypnos là hợp lệ. –

+0

@Mike B: Xin lỗi, nhưng tôi không thấy quan điểm của bạn. Khi tôi đọc nó, ypnos đang nói về chi phí phân bổ - thời gian cần để cấp phát bộ nhớ cho đối tượng - vì vậy sự phản đối của tôi đối với nó vẫn đứng vững. –

-1

Tôi nghĩ bạn có thể preallocate một số lượng nhất định của các yếu tố cho container STL, do đó có một chi phí phân bổ bộ nhớ liên tục nếu bạn biết có bao nhiêu yếu tố sẽ được trong container.

+1

std :: bộ không có chức năng như vậy.Có lẽ bạn đang nghĩ đến std :: vector :: reserve? –

+0

@Don: vâng, tôi đã. –

0

Đặt thường được triển khai dưới dạng cây màu đen đỏ. Mỗi nút được phân bổ riêng biệt để thiết lập không lợi ích từ việc tái sử dụng. Đó là lý do tại sao cả hai cách tiếp cận có hiệu suất gần như giống nhau. Gọi "tempSet.clear()" sẽ mất khoảng thời gian đó để phá hủy đối tượng. Cách tiếp cận đầu tiên tốt hơn vì nó có cùng hiệu suất và tuân theo một kiểu lập trình an toàn hơn.

Bạn có thể tìm thấy một cuộc thảo luận chung về container khác tại đây: Reusing a container o creating a new one

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