2010-08-15 47 views
13

Gần đây tôi đã đánh giá bộ thu gom rác .NET 4, phân bổ mạnh mẽ từ nhiều luồng. Khi các giá trị được phân bổ được ghi lại trong một mảng, tôi quan sát thấy không có khả năng mở rộng giống như tôi đã mong đợi (vì hệ thống cho phép truy cập đồng bộ vào một thế hệ cũ được chia sẻ). Tuy nhiên, khi các giá trị được phân bổ đã được loại bỏ ngay lập tức, tôi đã sợ hãi khi quan sát không có khả năng mở rộng sau đó!Khả năng mở rộng của bộ thu gom rác .NET 4

Tôi đã dự kiến ​​trường hợp tạm thời để chia tỷ lệ gần như tuyến tính bởi vì mỗi luồng chỉ cần xóa sạch gen0 sạch và bắt đầu lại mà không tranh giành bất kỳ tài nguyên được chia sẻ nào (không có gì tồn tại cho thế hệ cũ và không có bộ nhớ cache L2 nhớ vì gen0 dễ dàng khớp với L1 cache).

Ví dụ, this MSDN article says:

phân bổ đồng bộ hóa miễn Trên một hệ thống đa xử, thế hệ 0 của heap quản lý được chia thành nhiều đấu trường bộ nhớ sử dụng một đấu trường cho mỗi thread. Điều này cho phép nhiều chủ đề để thực hiện phân bổ đồng thời để truy cập độc quyền vào heap là không cần thiết.

Ai đó có thể xác minh các phát hiện của tôi và/hoặc giải thích sự khác biệt này giữa các dự đoán và quan sát của tôi không?

+3

Xác định ý bạn là "không có khả năng mở rộng". –

+7

bạn nên đăng phương pháp chính xác của mình, những gì bạn đã đo lường, cách bạn đo lường và đo lường giá trị. –

+2

Tôi đoán ở đây, nhưng tôi có lẽ Jon Harrop đã chạy thử nghiệm của mình trên một máy tính N-core và làm điểm chuẩn của mình với từ n = 1 đến N chủ đề. Tỷ lệ là sau đó tốc độ điểm chuẩn thay đổi như thế nào với n. –

Trả lời

11

Không phải là câu trả lời hoàn chỉnh cho câu hỏi, nhưng chỉ để làm sáng tỏ một số quan niệm sai: .NET GC chỉ đồng thời ở chế độ máy trạm. Ở chế độ máy chủ, nó sử dụng GC song song stop-the-world. Thêm chi tiết here. Các vườn ươm riêng biệt trong .NET chủ yếu là để tránh đồng bộ hóa về phân bổ; họ vẫn là một phần của đống toàn cầu và không thể được thu thập riêng biệt.

+1

"chúng vẫn là một phần của đống toàn cầu và không thể được thu thập riêng biệt". Đây chính xác là những gì tôi cần biết. Cảm ơn bạn! –

3

hoặc giải thích sự khác biệt này giữa các dự đoán và quan sát của tôi?

Đo điểm chuẩn là khó.
Đo điểm chuẩn hệ thống con không nằm trong tầm kiểm soát hoàn toàn của bạn thậm chí còn khó hơn.

12

Không chắc chắn về điều này và chính xác những gì bạn đã thấy trên máy của mình. Tuy nhiên, có hai phiên bản khác nhau của CLR trên máy của bạn. Mscorwks.dll và mscorsvc.dll. Trước đây là một trong những bạn nhận được khi bạn chạy chương trình của bạn trên một trạm làm việc, sau này trên một trong các phiên bản máy chủ của Windows (như Windows 2003 hoặc 2008).

Phiên bản trạm làm việc là loại máy tính địa phương của bạn, nó không gobble tất cả các tài nguyên máy. Bạn vẫn có thể đọc email của mình trong khi GC đang diễn ra. Phiên bản máy chủ được tối ưu hóa để mở rộng trên phần cứng cấp máy chủ. Rất nhiều RAM (GC không khởi động nhanh) và nhiều lõi CPU (rác được thu thập trên nhiều lõi). Bài báo được trích dẫn của bạn có thể nói về phiên bản máy chủ.

Bạn có thể chọn phiên bản máy chủ trên máy trạm của mình, sử dụng phần tử <gcServer> trong tệp .config của bạn.

4

Tôi có thể gây nguy hiểm cho một vài phỏng đoán về những gì đang xảy ra.

(1) Nếu bạn có một chuỗi duy nhất và có M không gian trống trong thế hệ 0, thì GC sẽ chỉ chạy khi M byte đã được cấp phát.

(2) Nếu bạn có N luồng và GC phân chia thế hệ 0 thành không gian N/M cho mỗi luồng, GC sẽ kết thúc chạy mỗi khi một luồng phân bổ N/M byte. Các showstopper ở đây là GC cần phải "ngăn chặn thế giới" (tức là, đình chỉ tất cả các chủ đề đang chạy) để đánh dấu tài liệu tham khảo từ bộ chủ đề của chủ đề. Đây không phải là rẻ. Vì vậy, không chỉ GC sẽ chạy thường xuyên hơn, nó sẽ làm nhiều công việc hơn cho mỗi bộ sưu tập.

Một vấn đề khác, tất nhiên, là các ứng dụng đa luồng thường không thân thiện với bộ nhớ cache, điều này cũng có thể làm giảm hiệu suất của bạn.

Tôi không nghĩ đây là vấn đề .NET GC, thay vào đó là vấn đề với GC nói chung. Một đồng nghiệp đã từng chạy một chuẩn "ping pong" đơn giản gửi các thông điệp số nguyên đơn giản giữa hai luồng sử dụng SOAP. Điểm chuẩn chạy nhanh gấp hai lần khi hai luồng trong các quy trình riêng biệt vì việc phân bổ và quản lý bộ nhớ đã hoàn toàn tách rời!

+0

@Rafe: "GC cần phải ngăn chặn thế giới". Bạn có chắc không? Tôi có thể tưởng tượng thiết kế mà tất cả rễ của các đối tượng trong thế hệ vườn ươm đều nằm trong các biến toàn cục, (một) ngăn xếp luồng cục bộ và một bộ nhớ được tạo ra bởi hàng rào ghi. –

+1

@Jon: hơi trễ một chút, vì vậy tôi có thể rời khỏi cơ sở ở đây, nhưng sẽ không yêu cầu mỗi máy đăng ký để được hỗ trợ bởi một ngăn xếp toàn cầu hoặc ngăn xếp thay vì phủ nhận rất nhiều tối ưu hóa tạo mã? Ngoài ra, viết rào cản không hề rẻ. Những gì tôi có trong tâm trí này là: GC không có cách nào để biết rằng thread tôi đã không truyền đạt một tham chiếu đến một đối tượng địa phương để thread j, vì vậy nó cần phải kiểm tra rễ của j để tìm tài liệu tham khảo vào vườn ươm của tôi. Dù bằng cách nào, việc đọc bài viết được liên kết của tôi trong phần "Hiệu suất cho các ứng dụng đa luồng" là .NET GC là một kiểu stop-the-world. – Rafe

+0

@Rafe: "sẽ không yêu cầu mỗi máy đăng ký để được hỗ trợ bởi một ngăn xếp toàn cầu hoặc ngăn xếp thay vì phủ nhận rất nhiều tối ưu hóa tạo mã". Không, tôi đã sử dụng kỹ thuật này trong HLVM và nó tạo ra mã rất nhanh. –

4

Rất nhanh, dễ thấy (thẳng tại gốc, gán giá trị rỗng) và các bản phát hành lớn có thể lừa GC trở nên háo hức và toàn bộ ý tưởng về vùng nhớ cache cục bộ là một giấc mơ đẹp :-) Ngay cả khi bạn đã tách biệt hoàn toàn thread-local heaps (mà bạn không) bảng handle-pointer sẽ vẫn phải hoàn toàn dễ bay hơi chỉ để làm cho an toàn cho các kịch bản đa CPU chung. Oh và hãy nhớ rằng có rất nhiều chủ đề, bộ nhớ cache CPU được chia sẻ, nhu cầu hạt nhân có ưu tiên vì vậy nó không phải tất cả chỉ dành cho bạn :-)

Cũng hãy cẩn thận rằng "heap" với con trỏ đôi có 2 phần - khối bộ nhớ để cung cấp và bảng con trỏ xử lý (để các khối có thể được di chuyển nhưng mã của bạn luôn có một địa chỉ). Bảng như vậy là một tài nguyên cấp quy trình rất quan trọng nhưng rất gọn gàng và chỉ là cách duy nhất để nhấn mạnh nó là làm ngập nó với các bản phát hành nhanh - vì vậy bạn đã thực hiện :-))

Nói chung quy tắc của GC là - rò rỉ :-) Không phải mãi mãi tất nhiên, nhưng loại cho miễn là bạn có thể. Nếu bạn nhớ cách mọi người đi xung quanh nói "không ép buộc các bộ sưu tập GC"? Đó là một phần của câu chuyện. Ngoài ra bộ sưu tập "dừng lại trên thế giới" thực sự hiệu quả hơn nhiều so với "đồng thời" và được sử dụng để được biết đến bởi một tên đẹp hơn của chu kỳ ăn cắp hoặc hợp tác sheduler. Chỉ có giai đoạn đánh dấu cần đóng băng bộ lập lịch và trên máy chủ có một loạt các chủ đề đang thực hiện (N lõi không hoạt động :-) Lý do duy nhất cho người khác là nó có thể thực hiện các hoạt động thời gian thực như chơi video jittery , giống như lượng tử luồng dài hơn. Vì vậy, một lần nữa nếu bạn cạnh tranh với cơ sở hạ tầng trên các vụ nổ CPU ngắn và thường xuyên (phân bổ nhỏ, hầu như không có công việc, phát hành nhanh), điều duy nhất bạn sẽ thấy/đo sẽ là tiếng ồn GC và JIT.

Nếu điều này là dành cho một cái gì đó thực sự, tức là không chỉ thử nghiệm, tốt nhất bạn có thể làm là sử dụng mảng giá trị lớn trên ngăn xếp (cấu trúc). Họ không thể bị ép buộc vào heap và là local như một local có thể nhận được, và không phải chịu bất kỳ backdoor di chuyển => cache phải yêu chúng :-) Điều đó có nghĩa là chuyển sang chế độ "không an toàn", sử dụng con trỏ bình thường và có thể làm một chút phân bổ trên của riêng bạn (nếu yopu cần một cái gì đó đơn giản như danh sách) nhưng đó là một mức giá nhỏ để trả cho đá GC ra :-) Cố gắng để buộc dữ liệu vào bộ nhớ cache cũng phụ thuộc vào giữ ngăn xếp của bạn nạc khác - nhớ rằng bạn đều không cô đơn. Ngoài ra cho chủ đề của bạn một số công việc đó là giá trị ít nhất một số lượng tử berween phát hành có thể giúp đỡ. Kịch bản trường hợp xấu nhất sẽ là nếu bạn phân bổ và giải phóng trong một lượng tử signle.