2010-05-03 14 views
29

Các loại bộ sưu tập nhất định trong .Net có tham số hàm khởi tạo "Công suất ban đầu" tùy chọn. Ví dụ:Dung lượng bộ sưu tập ban đầu, ví dụ: Từ điển, Danh sách

Dictionary<string, string> something = new Dictionary<string,string>(20); 

List<string> anything = new List<string>(50); 

tôi dường như không thể tìm thấy những gì mặc định công suất ban đầu là dành cho các đối tượng này trên MSDN.

Nếu tôi biết tôi sẽ chỉ lưu trữ 12 hoặc hơn các mục trong từ điển, không có ý nghĩa gì khi đặt công suất ban đầu là 20? Lý do của tôi là, giả định rằng khả năng phát triển như nó cho StringBuilder, tăng gấp đôi mỗi lần công suất bị tấn công, và mỗi lần phân bổ lại tốn kém, tại sao không đặt trước kích thước cho thứ bạn biết sẽ giữ dữ liệu của bạn , với một số phòng phụ chỉ trong trường hợp? Nếu công suất ban đầu là 100, và tôi biết tôi sẽ chỉ cần một tá hoặc hơn, nó có vẻ như phần còn lại của bộ nhớ đó được phân bổ cho không có gì.

Trả lời

60

Nếu các giá trị mặc định không được ghi lại, lý do có khả năng là công suất ban đầu tối ưu là chi tiết thực hiện và có thể thay đổi giữa các phiên bản khung công tác. Tức là, bạn không nên viết mã giả định một giá trị mặc định nhất định.

Nhà xây dựng tình trạng quá tải với công suất là các trường hợp mà bạn biết rõ hơn số so với số lượng mặt hàng sẽ được mong đợi. Ví dụ: nếu bạn tạo một tập hợp gồm 50 giá trị và biết rằng con số này sẽ không bao giờ tăng, bạn có thể khởi tạo bộ sưu tập với dung lượng 50, vì vậy nó sẽ không phải thay đổi kích thước nếu dung lượng mặc định thấp hơn.

Điều đó nói rằng, bạn có thể xác định giá trị mặc định bằng Reflector. Ví dụ, trong .NET 4.0 (và có lẽ phiên bản trước cũng),

  • một danh sách <T> được khởi tạo với công suất 0. Khi mục đầu tiên được thêm vào, nó được reinitialized để công suất 4. Sau đó, bất cứ khi nào công suất đạt được, công suất được tăng gấp đôi.

  • từ điển <T> được intialized với công suất là 0. Nhưng nó sử dụng một thuật toán hoàn toàn khác nhau để tăng dung lượng: nó làm tăng khả năng luôn luôn là số nguyên tố.

+6

Tính toán số nguyên tố có khả năng đối phó với các va chạm băm và thăm dò vị trí đầu vào. Tùy thuộc vào cơ chế nội bộ nếu chúng chỉ lưu trữ một giá trị tại mỗi băm thì chúng cần vị trí lưu trữ thứ cấp. Nếu bạn không sử dụng số nguyên tố thì bạn có khả năng tìm thấy một băm mà bạn không thể chèn vào. – Matt

+5

Từ điển sử dụng chuỗi. Các kích thước bảng số nguyên tố bù cho các hàm băm kém. Hàm băm tốt tạo ra các bản phân phối ngẫu nhiên; sức mạnh của hai kích thước bảng được sử dụng trong bảng băm hiện đại (bảng băm .net được dựa trên bảng băm Java, cũng sử dụng số nguyên tố, vì đó là một cách cũ để thực hiện nó, trong những ngày hàm băm kém). Vì Microsoft không cung cấp các phương thức kết hợp băm được xây dựng, nhiều hàm băm được xây dựng trong nhà tạo ra các bản phân phối kém, nên lựa chọn số nguyên tố bù trừ, đôi khi - cho đến khi hàm băm tạo bội số của số nguyên tố. –

8

Kiểm tra nguồn, công suất mặc định cho cả List<T>Dictionary<TKey, TValue> là 0.

+4

Trong .Net 4.5 công suất bổ sung thực sự là 3. Có, hàm khởi tạo mặc định gọi một hàm tạo quá tải với giá trị công suất là 0, nhưng khi hàm khởi tạo gọi phương thức khởi tạo, kích thước được đặt thành 3. Kích thước thực của từ điển được xác định từ một cuộc gọi đến HashHelpers.GetPrime (công suất) trả về số nguyên tố tiếp theo lớn hơn công suất được cung cấp. Như vậy, trong .Net 4.5 dung lượng ban đầu cho một từ điển là 3. Danh sách có dung lượng mặc định là 0, nhưng dung lượng sẽ là 4, sau khi thêm mục đầu tiên vào danh sách. –

6

Nếu bạn biết kích thước, sau đó nói với nó; một tối ưu hóa nhỏ trong hầu hết các trường hợp "nhỏ", nhưng hữu ích cho các bộ sưu tập lớn hơn. Tôi sẽ chủ yếu là lo lắng về điều này nếu tôi đang ném một lượng "phong nha" dữ liệu trong, vì nó có thể sau đó tránh phải phân bổ, sao chép và thu thập nhiều mảng.

Hầu hết các bộ sưu tập thực sự sử dụng chiến lược tăng gấp đôi.

1

Một vấn đề khác với ConcurrentDictionary (hiện tại) và sử dụng hàm tạo của nó để đặt kích thước ban đầu là hiệu suất của nó dường như bị cản trở.

Ví dụ: here's some example code and benchmarks Tôi đã thử.

Tôi đã chạy mã trên máy của mình và nhận được kết quả tương tự.

Tức là, khi kích thước ban đầu được chỉ định, nó không làm gì để tăng tốc độ của ConcurrentDictionary khi thêm đối tượng. Về mặt kỹ thuật, tôi nghĩ rằng nó nên bởi vì nó không phải mất thời gian hoặc nguồn lực để thay đổi kích cỡ chính nó. Có, nó có thể không chạy nhanh như một từ điển thông thường, nhưng tôi vẫn mong đợi một ConcurrentDictionary với kích thước ban đầu được thiết lập để có hiệu suất phù hợp, nhanh hơn ConcurrentDictionary không có kích thước ban đầu, đặc biệt là khi người ta biết trước số lượng vật phẩm sẽ được thêm vào nó.

Vì vậy, đạo đức của câu chuyện được đặt kích thước ban đầu không phải lúc nào cũng đảm bảo cải thiện hiệu suất.