2012-04-15 33 views
11

Tôi đang sử dụng map<MyStruct, I*> map1;. Rõ ràng 9% tổng thời gian ứng dụng của tôi được chi tiêu trong đó. Cụ thể trên một dòng của một trong những chức năng chính của tôi. Bản đồ không phải là rất lớn (< 1k hầu như luôn luôn, < 20 là phổ biến).hiệu suất bản đồ stl?

Có triển khai thay thế nào mà tôi có thể muốn sử dụng không? Tôi nghĩ rằng tôi không nên viết của riêng tôi nhưng tôi có thể nếu tôi nghĩ rằng đó là một ý tưởng tốt.

Thông tin bổ sung: Tôi luôn kiểm tra trước khi thêm phần tử. Nếu một khóa tồn tại tôi cần báo cáo một vấn đề. Hơn sau một thời điểm tôi sẽ sử dụng bản đồ rất nhiều cho tra cứu và sẽ không thêm bất kỳ yếu tố nào nữa.

+7

Nếu không có mã nguồn, chúng tôi thực sự không thể biết được. Ngoài ra, hãy xem phiên bản 'insert' trả về một cặp (điều này sẽ trả lời câu hỏi thứ hai của bạn) –

+1

Bạn có thể chia sẻ thông tin về hàm so sánh của bạn trên' MyStruct' mà bản đồ sử dụng không? – amit

+0

Bạn có thể cung cấp thêm một chút thông tin về những gì bạn đang làm trong chức năng được đề cập không? Vì độ phức tạp tra cứu của bản đồ là O (log n), tôi không chắc chắn sự cải thiện sẽ đến từ đâu. –

Trả lời

6

Sẽ nhanh hơn khi chỉ cần thực hiện insert và kiểm tra xem pair.second có phải là false nếu khóa đã tồn tại không?

như thế này

if (myMap.insert(make_pair(MyStruct, I*)).second == false) 
{ 
    //report error 
} 
else 
    // inserted new value 

chứ không phải làm một cuộc gọi find mỗi lần?

+0

Ý tưởng tuyệt vời. Mặc dù thêm memebers không phải là dòng với vấn đề hiệu suất.Tôi sẽ thêm vào và xem hiệu suất –

+0

Nếu bạn hoãn tìm kiếm xem bạn có thực sự có xung đột hay không, bạn có thể loại bỏ một số lần tra cứu không cần thiết trừ khi bạn đang làm điều gì đó khá kỳ quặc và tạo nhiều giá trị trùng lặp hơn những người độc đáo. Phương thức 'insert' tôi trả về chi tiết một cặp với' first' là một iterator với giá trị mới hoặc giá trị trùng lặp, thứ hai là một boolean với 'true' có nghĩa là thành công và' false' có nghĩa là mục trùng lặp. – EdChum

+0

Tôi cảm thấy một chút ngớ ngẩn không làm chèn theo cách này. Tôi phải vội vàng hoặc nghĩ đó là mã ném. –

2

Cách bạn đang sử dụng bản đồ, bạn đang thực hiện tra cứu trên cơ sở cá thể MyStruct và tùy thuộc vào việc triển khai cụ thể của bạn, so sánh được yêu cầu có thể hoặc không tốn kém.

+0

hmm, tôi không nghĩ rằng tôi cần nó theo một thứ tự cụ thể. Tôi nhận ra hầu hết các mã đã được kiểm tra nếu hai biến là null và tôi dount nghĩ rằng nó bao giờ được. Chỉ cần loại bỏ kiểm tra đó làm cho nó đủ nhanh để biến mất khỏi hồ sơ (tôi ngạc nhiên). Nếu nó xuất hiện trở lại, tôi sẽ chơi với hàm so sánh hơn. +1 và có thể chấp nhận. –

4

Nó có thể là một cảnh quay dài, nhưng đối với các bộ sưu tập nhỏ, đôi khi yếu tố quan trọng nhất là hiệu suất cache.

Kể từ std::map thực hiện một cây đỏ-đen, đó là [AFAIK] không phải là rất nhớ cache hiệu quả - có thể thực hiện bản đồ dưới dạng std::vector<pair<MyStruct,I*>> sẽ là một ý tưởng tốt, và sử dụng tìm kiếm nhị phân có [thay vì bản đồ look-up] , ít nhất nó sẽ hiệu quả một khi bạn bắt đầu chỉ tìm kiếm [ngừng chèn các yếu tố], vì std::vector có nhiều khả năng phù hợp với bộ nhớ cache hơn so với map.

Yếu tố này [cpu-cache] thường bị bỏ qua và được ẩn là hằng số trong ký hiệu O lớn, nhưng đối với các bộ sưu tập lớn, nó có thể có tác động lớn.

+0

Tôi có thể có một lớp học sử dụng hai vectơ nội bộ chứ không phải là một cặp và làm cho nó trông giống như một bản đồ. –

+1

@ acidzombie24: 'cặp' chỉ là một gợi ý. Về hai vectơ: Tôi đồng ý, nó có thể sẽ thực sự tốt hơn sau đó là 'vectơ' của' cặp '[ít trường hơn -> ít sử dụng bộ nhớ hơn -> nhiều khả năng toàn bộ bản đồ sẽ phù hợp với bộ nhớ cache]. Câu trả lời chỉ nhằm nhấn mạnh tầm quan trọng của bộ nhớ đệm trên các bộ sưu tập nhỏ, và để nhắc nhở nó thường quan trọng hơn rất nhiều sau đó ký hiệu O lớn cho chúng, vì các hằng số không nên bỏ qua. – amit

+1

Chúng tôi đã thấy những cải tiến lớn trong mã của chúng tôi trong một số trường hợp nhất định khi chuyển từ bản đồ sang vectơ. Chúng tôi nghi ngờ hiệu suất bộ nhớ đệm lớn hơn nhiều với vectơ. –

1

Có triển khai thay thế nào mà tôi có thể muốn sử dụng không? Tôi nghĩ rằng tôi không nên viết của riêng tôi nhưng tôi có thể nếu tôi nghĩ rằng đó là một ý tưởng tốt.

Nếu bạn hiểu vấn đề đủ tốt, bạn nên nêu chi tiết cách triển khai của bạn sẽ vượt trội.

Có phải cấu trúc thích hợp là map? Nếu có, thì việc triển khai thư viện chuẩn của bạn sẽ có chất lượng tốt (được tối ưu hóa tốt).

Có thể đơn giản hóa việc so sánh MyStruct không?

Sự cố - thay đổi kích thước ở đâu? tra cứu?

Bạn có thu nhỏ bản sao và chỉ định chi phí cho cấu trúc của mình không?

+0

Sự cố: Tra cứu. Cấu trúc đúng: Có thể. Tôi cần phải tìm một cấu trúc bằng phím (mà cần phải so sánh với cấu trúc khác) và đối tượng giao diện liên kết với nó. Tôi không nghĩ rằng trật tự là một vấn đề vì nó thường nhỏ. Sao chép/Gán: Vâng, tôi chỉ định bằng cách sao chép một ptr. Không thay đổi của nó vì vậy tôi không phải lo lắng về việc nó bị xóa. –

+0

Tôi đã kiểm tra nếu cả hai cấu trúc là null trong fp cmp của tôi. Loại bỏ nó đã làm cho nó đủ nhanh để không hiển thị nữa. 1 cho câu trả lời tổng thể. –

5

Thay vì map bạn có thể thử unordered_map sử dụng khóa băm thay cho cây để tìm phần tử. This answer đưa ra một số gợi ý khi thích unordered_map trên map.

+3

Đối với các bộ sưu tập nhỏ, bản đồ băm là * thường * chậm hơn sau đó là màu đỏ-đen-cây, vì vậy tôi hy vọng việc sử dụng nó trong trường hợp này chỉ có tác động tiêu cực. – amit

+0

@amit: Ngay cả đối với một bộ sưu tập nhỏ với 20 yếu tố, [so sánh đơn giản này] (http://ideone.com/YupK4) - dưới sự hạn chế của một loại khóa đơn giản - mang lại cho tôi hiệu suất tốt hơn cho 'unordered_set '. Một so sánh thực sự sẽ bao gồm 'MyStruct' và hàm băm cho nó. –

+0

Tôi đã xem unordered_map. Sau khi đọc thông báo lỗi (lớn của nó ...) tôi nhận ra tôi cần phải thực hiện một hàm băm. Tôi không có ý tưởng làm thế nào để làm điều đó. Làm thế nào để tôi băm một char *? Có thể dài từ 1 đến 20chars (hoặc hơn) –

35

Trước tiên, bạn cần phải hiểu bản đồ là gì và các hoạt động mà bạn đang thực hiện đại diện. A std::map là cây nhị phân cân bằng, tra cứu sẽ thực hiện các hoạt động O(log N), mỗi thao tác là so sánh các phím cộng với một số phụ mà bạn có thể bỏ qua trong hầu hết các trường hợp (quản lý con trỏ). Chèn mất khoảng thời gian tương tự để xác định vị trí điểm chèn, cộng với phân bổ của nút mới, chèn thực tế vào cây và cân bằng lại. Độ phức tạp lại một lần nữa là O(log N) mặc dù các hằng số ẩn cao hơn.

Khi bạn cố gắng xác định xem liệu khóa có nằm trong bản đồ trước khi chèn hay không, bạn sẽ phải trả chi phí tra cứu và nếu nó không thành công, chi phí giống nhau để xác định vị trí chèn. Bạn có thể tránh chi phí thêm bằng cách sử dụng std::map::insert trả về một cặp với một trình lặp và một bool cho bạn biết liệu việc chèn thực sự xảy ra hay phần tử đã có sẵn. Ngoài ra, bạn cần phải hiểu cách so sánh các khóa của mình tốn kém, những gì nằm ngoài câu hỏi hiển thị (MyStruct có thể chỉ giữ một int hoặc một nghìn trong số đó), đó là thứ bạn cần phải đưa vào tài khoản. Cuối cùng, có thể là trường hợp mà map không phải là cấu trúc dữ liệu hiệu quả nhất cho nhu cầu của bạn và bạn có thể cân nhắc sử dụng std::unordered_map (bảng băm) đã dự kiến ​​chèn thời gian liên tục (nếu hàm băm không phải là khủng khiếp) hoặc cho các tập dữ liệu nhỏ ngay cả một mảng được sắp xếp đơn giản (hoặc std::vector) mà bạn có thể sử dụng tìm kiếm nhị phân để xác định vị trí các yếu tố (điều này sẽ giảm số lượng phân bổ, với chi phí chèn tiền đắt hơn) các loại được tổ chức đủ nhỏ có thể đáng giá)

Như thường lệ với hiệu suất, đo lường và sau đó cố gắng hiểu thời gian đang được sử dụng. Cũng lưu ý rằng 10% thời gian dành cho một hàm hoặc cấu trúc dữ liệu cụ thể có thể là rất nhiều hoặc gần như không có gì, tùy thuộc vào ứng dụng của bạn là gì. Ví dụ: nếu ứng dụng của bạn chỉ thực hiện tra cứu và chèn vào bộ dữ liệu và chỉ mất 10% CPU bạn có rất nhiều để tối ưu hóa ở mọi nơi khác!

+0

Câu trả lời hay. Một gợi ý không có thứ tự (băm) có thể xấu vì kích cỡ của bảng nhỏ. Tôi chắc chắn sẽ sử dụng chèn vào vị trí khác của tôi. Bạn nhấn vào tất cả các điểm quan trọng –

+2

Cách khác để chèn mù bạn có thể sử dụng 'lower_bound', kiểm tra khóa, và sau đó sử dụng chèn gợi ý (nếu bạn muốn tránh việc xây dựng đối tượng có khả năng tốn kém). –

+0

Cấu trúc thực sự là một mẫu trình bao bọc nhanh mà tôi đã viết có chứa T *. Vấn đề là các op như '<' và '==' sẽ làm '* ptr OP * other' để nó không so sánh với địa chỉ ptr. Nó làm cho cuộc sống của tôi vô cùng dễ dàng hơn khi sử dụng các thùng chứa. Đó là một gợi ý tốt. +1 @KerrekSB –

1

Như đã nêu trong các nhận xét, không có mã thích hợp, có rất ít câu trả lời phổ quát để cung cấp cho bạn. Tuy nhiên, nếu MyStruct thực sự rất lớn, việc sao chép ngăn xếp có thể tốn kém. Có lẽ nó làm cho tinh thần để lưu trữ các con trỏ tới MyStruct và thực hiện so sánh cơ chế của riêng bạn:

template <typename T> struct deref_cmp { 
    bool operator()(std::shared_ptr<T> lhs, std::shared_ptr<T> rhs) const { 
    return *lhs < *rhs; 
    } 
}; 

std::map<std::shared_ptr<MyStruct>, I*, deref_cmp<MyStruct>> mymap; 

Tuy nhiên, đây là một cái gì đó bạn sẽ phải cấu. Nó có thể tăng tốc độ.

Bạn sẽ nhìn lên một yếu tố như

template <typename T> struct NullDeleter { 
    void operator()(T const*) const {} 
}; 
// needle being a MyStruct 
mymap.find(std::shared_ptr<MyStruct>(&needle,NullDeleter())); 

Không cần điều này để nói, có nhiều tiềm năng để tối ưu hóa.

+0

MyStruct thực sự là T * trong đó T là mẫu;). Xem bình luận đầu tiên của tôi để Johann Gerell trả lời –

+1

Vâng, trong trường hợp đó bạn không phải bận tâm với chuyển hướng này. – bitmask

+0

yep. vì vậy +1 cho anyways đề xuất :) –

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