2012-01-17 4 views
20

Tôi có một số vector<int> với 10.000.000 (10 triệu) phần tử và máy trạm của tôi có bốn lõi. Có một hàm, gọi là ThrFunc, hoạt động trên một số nguyên. Giả sử rằng thời gian chạy cho ThrFunc cho mỗi số nguyên trong vector<int> gần như giống nhau.Cách tốt nhất để xác định số lượng đề tài để cháy trong máy bằng lõi là gì? (C++)

Tôi nên xác định số lượng chủ đề tối ưu để kích hoạt như thế nào? Câu trả lời đơn giản như số phần tử chia cho số lõi? Hay có tính toán tinh tế hơn?

Editing để cung cấp thêm thông tin

  • Không cần chặn; mỗi yêu cầu chức năng chỉ cần đọc quyền truy cập
+2

Điều đó sẽ có nhiều chủ đề! Tôi nghĩ bạn có nghĩa là số lõi, phải không? – dasblinkenlight

+0

Giả sử rằng tất cả các thao tác trên các số nguyên có thể xảy ra hoàn toàn đồng thời, bạn chỉ cần chia cho số lõi. Nó là khó khăn hơn nhiều để ước tính khi công việc không thể được thực hiện đồng thời. –

+1

Các chủ đề này có thực hiện bất kỳ (chặn) I/O hoặc bất kỳ hoạt động chặn nào như thông tin liên lạc mạng hoặc cơ sở dữ liệu không? Nếu không, thì có thể số lõi tối ưu là N. Trong trường hợp của bạn, 4. Nếu không, 2N hoặc 3N sẽ đáng thử nghiệm - trong khi một luồng đang làm I/O, một luồng khác có thể hoạt động. – selbie

Trả lời

23

Số lượng tối ưu của chuỗi có thể là số lõi trong máy hoặc số lõi của bạn gấp hai lần.

Trong điều kiện trừu tượng hơn, bạn muốn có thông lượng cao nhất có thể. Nhận được thông lượng cao nhất đòi hỏi các điểm tranh chấp ít nhất giữa các chủ đề (kể từ khi vấn đề ban đầu là song song trivially). Số lượng các điểm tranh chấp có thể là số lượng các chủ đề chia sẻ một lõi hoặc hai lần đó, kể từ khi một lõi có thể chạy một hoặc hai chủ đề hợp lý (hai với siêu phân luồng).

Nếu khối lượng công việc của bạn sử dụng tài nguyên mà bạn có ít hơn bốn sẵn có (ALUs trên Bulldozer? Truy cập đĩa cứng?) Thì số lượng chuỗi bạn tạo sẽ bị giới hạn bởi điều đó.

Cách tốt nhất để tìm ra câu trả lời đúng là, với tất cả các câu hỏi về phần cứng, để kiểm tra và tìm hiểu.

+0

Cảm ơn bạn đã phản hồi. Đã chấp nhận. – Shredderroy

+0

Nếu tính toán của bạn sẽ sử dụng cùng một dữ liệu trên mỗi luồng, có lẽ tốt nhất là bỏ qua siêu phân luồng, hoặc thậm chí vô hiệu hóa nó hoàn toàn. Các dữ liệu cho cả hai chủ đề có thể sẽ được lưu trữ khá nhanh chóng, do đó sẽ không gian hàng, do đó HT sẽ không bao giờ có thời gian để thực sự làm bất cứ điều gì. –

+0

+1 Lời khuyên tuyệt vời. – Tudor

4

Giả sử ThrFunc là CPU bị ràng buộc, sau đó bạn muốn có thể một luồng trên mỗi lõi và chia các phần tử giữa chúng.

Nếu có phần tử I/O cho hàm thì câu trả lời phức tạp hơn, vì bạn có thể có một hoặc nhiều luồng trên mỗi lõi chờ I/O trong khi một phần tử khác đang thực thi. Làm một số xét nghiệm và xem điều gì xảy ra.

+0

Giả sử bạn không muốn làm bất cứ điều gì khác với máy của bạn tất nhiên :-) – paxdiablo

+0

@paxdiablo - Tất nhiên, mặc dù hệ điều hành sẽ cung cấp thời gian CPU cho các quá trình khác. –

2

Số lượng chủ đề tối ưu phải bằng số lõi, trong đó tình hình khả năng tính toán của mỗi lõi sẽ được sử dụng đầy đủ, nếu tính toán trên mỗi phần tử là độc lập.

11

Borealid's answer bao gồm kiểm tra và tìm hiểu, không thể đánh bại như lời khuyên.

Nhưng có lẽ nhiều hơn để thử nghiệm điều này hơn bạn có thể nghĩ: bạn muốn chủ đề của bạn để tránh tranh chấp cho dữ liệu bất cứ nơi nào có thể. Nếu dữ liệu hoàn toàn chỉ đọc, thì bạn có thể thấy hiệu suất tốt nhất nếu các chuỗi của bạn đang truy cập dữ liệu "tương tự" - đảm bảo đi qua dữ liệu trong các khối nhỏ tại một thời điểm, vì vậy mỗi chuỗi đang truy cập dữ liệu từ same pages over and over again. Nếu dữ liệu là hoàn toàn chỉ đọc, thì sẽ không có vấn đề gì nếu mỗi lõi có bản sao riêng của các dòng bộ nhớ cache. (Mặc dù điều này có thể không tận dụng tối đa bộ nhớ cache của từng lõi.)

Nếu dữ liệu được sửa đổi theo bất kỳ cách nào, bạn sẽ thấy các cải tiến hiệu suất đáng kể nếu bạn giữ chủ đề cách từ mỗi khác .Hầu hết bộ nhớ lưu trữ dữ liệu dọc theo cache lines và bạn tuyệt vọng muốn giữ lại mỗi cache line from bouncing among CPUs để có hiệu suất tốt. Trong trường hợp đó, bạn có thể muốn giữ các luồng khác nhau chạy trên dữ liệu thực sự xa nhau để tránh việc chạy với nhau. Vì vậy: nếu bạn đang cập nhật dữ liệu trong khi làm việc trên nó, tôi khuyên bạn nên có N hoặc 2 * N thực hiện (đối với N lõi), bắt đầu chúng với SIZE/N * M làm điểm bắt đầu của chúng, cho các chủ đề từ 0 đến M. (0, 1000, 2000, 3000, cho bốn chủ đề và 4000 đối tượng dữ liệu.) Điều này sẽ cho bạn cơ hội tốt nhất để đưa các dòng bộ nhớ cache khác nhau vào mỗi lõi và cho phép các cập nhật tiếp tục mà không có dòng bộ nhớ cache nảy:

+--------------+---------------+--------------+---------------+--- ... 
| first thread | second thread | third thread | fourth thread | first ... 
+--------------+---------------+--------------+---------------+--- ... 

Nếu bạn không cập nhật dữ liệu khi làm việc trên nó, bạn có thể muốn bắt đầu N hoặc 2 * N đề thi (đối với N lõi), bắt đầu chúng với 0, 1, 2, 3 , vv .. và di chuyển từng cái tiến lên bằng N hoặc 2 * N các phần tử với mỗi lần lặp. Điều này sẽ cho phép hệ thống bộ nhớ cache tìm nạp từng trang từ bộ nhớ một lần, điền vào bộ nhớ cache của CPU với dữ liệu gần như giống hệt nhau và hy vọng giữ cho mỗi lõi có dữ liệu mới.

+-----------------------------------------------------+ 
| 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 ... | 
+-----------------------------------------------------+ 

tôi cũng khuyên bạn nên sử dụng sched_setaffinity(2) trực tiếp trong mã của bạn để lực các chủ đề khác nhau để xử lý riêng của họ. Theo kinh nghiệm của tôi, Linux hướng tới keep each thread on its original processor rất nhiều, nó sẽ không di chuyển các tác vụ sang các lõi khác mà không hoạt động.

+0

Cảm ơn bạn rất nhiều vì đã giải thích. Về câu cuối cùng: Có vấn đề gì nếu tôi đang sử dụng Windows 7 hoặc Windows Server 2008 R2? – Shredderroy

+0

@Shredderroy: nó quan trọng trong đó 'sched_setaffinity (2)' là Unix (hoặc là Linux?) Cụ thể, trên Windows, nó sẽ là một chức năng khác. –

+0

@Shredderroy, Matthieu là chính xác; Tuy nhiên, Windows có thể thực hiện tốt công việc cân bằng giữa các CPU so với Linux. Kiểm tra thử nghiệm. :) – sarnold

1

Tôi đồng ý với các nhận xét trước. Bạn nên chạy thử nghiệm để xác định số lượng nào mang lại hiệu suất tốt nhất. Tuy nhiên, điều này sẽ chỉ mang lại hiệu suất tốt nhất cho hệ thống cụ thể mà bạn đang tối ưu hóa. Trong hầu hết các trường hợp, chương trình của bạn sẽ được chạy trên các máy của người khác, trên kiến ​​trúc mà bạn không nên tạo quá nhiều giả định.

Một cách tốt để số lượng xác định số lượng bài để bắt đầu sẽ được sử dụng

std::thread::hardware_concurrency() 

Đây là một phần của C++ 11 và nên mang lại số của lõi logic trong hệ thống hiện tại. Các lõi logic có nghĩa là số lõi vật lý - trong trường hợp bộ vi xử lý không hỗ trợ các chủ đề phần cứng (tức là Siêu phân luồng) - hoặc số lượng các chuỗi phần cứng.

Ngoài ra còn có chức năng Boost hoạt động tương tự, xem Programmatically find the number of cores on a machine.

0

Số lượng lõi (chủ đề) tối ưu có thể sẽ được xác định khi bạn đạt được độ bão hòa của hệ thống bộ nhớ (bộ đệm và RAM). Một yếu tố khác có thể xảy ra là khóa liên lõi (khóa vùng bộ nhớ mà các lõi khác có thể muốn truy cập, cập nhật và sau đó mở khóa) và mức độ hiệu quả của nó (bao lâu khóa và vị trí khóa nó bị khóa/mở khóa).

Một lõi đơn chạy một phần mềm chung có mã và dữ liệu không được chọn lọc cho đa lõi sẽ đến gần với bộ nhớ bão hòa một mình. Thêm nhiều lõi hơn, trong trường hợp như vậy, sẽ dẫn đến một ứng dụng chậm hơn.

Vì vậy, trừ khi mã của bạn tiết kiệm rất nhiều vào truy cập bộ nhớ, tôi đoán câu trả lời cho câu hỏi của bạn là một (1).